Search code examples
xslt-2.0xpath-2.0

Storing text values in Variable of previous nodes


Input

<?xml version="1.0" encoding="UTF-8"?>
<XML>
    <Concept>
        <Heading-1>This is First Heading</Heading-1>
    </Concept>
    <Concept>
        <Heading-2>This is Second Heading</Heading-2>
    </Concept>
    <Concept>
        <Heading-2>This is First Heading</Heading-2>
    </Concept>
 </XML>

Output should be

<?xml version="1.0" encoding="UTF-8"?>
<name>This_is_First_Heading</name>
<name>This_is_Second_Heading</name>
<name>1_This_is_First_Heading</name>

Stylesheet

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xd="http://www.oxygenxml.com/ns/doc/xsl" version="2.0">
    <xsl:template match="Concept">
        <xsl:variable name="name" 
                      select="./Heading-1/text() | ./Heading-2/text()"/>
        <xsl:variable name="name1" 
                      select="if //Cocept/Heading-1/text()=$name 
                              then concat(position(), '_' $name)} 
                              else $name"/>
        <name>
            <xsl:value-of select="replace($name, ' ', '_' )"/>
        </name>
    </xsl:template>
</xsl:stylesheet>

Problem: I need to print all the heading text values under "name" element. But if there is similar text value present for previous hading it should add position() number before the text values. I need to do it though variable only, if you can see in Variable name1 i am trying to put some logic which will compare the value of previous heading and if come across the similar text then put the position number, but somehow am not able to achieve that. Can you please help me write same logic in variable name1. Thanks in advance for your help.

______Edited_________

<xsl:template match="Concept">
    <xsl:variable name="name"
                  select="if (./Heading-1[1] |
                              ./Heading-2[1] |
                              ./Heading-3[1] |
                              ./Heading-4[1] |
                              ./Heading-5[1])
                          then normalize-space((Heading-1[1] |
                                                Heading-2[1] |
                                                Heading-3[1] |
                                                Heading-4[1] |
                                                Heading-5[1])
                                                /text()[position()=last()])
                          else normalize-space(./Heading-2[1]/text()[2])"/>
        <xsl:variable name="name1"
                      select="if (//Concept/Heading-3/text()
                                   [position()=last()] = $name)
                              then concat(position(), '_', $name)
                              else $name"></xsl:variable>
        <xsl:variable name="name2"
                      select="if (string-length($name5)=0)
                              then concat(position(), '_', $name5)
                              else $name5"/>
        <xsl:result-document href="XML/{replace($name2, ' ', '_')}.xml"
                             format="testing" validation="strip">

Solution

  • One of the simplest possible XSLT 2.0 solutions:

    <xsl:stylesheet version="2.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:strip-space elements="*"/>
     <xsl:template match="/*/*/*">
      <name>
        <xsl:sequence select=
         "concat(for $cnt in count(../preceding-sibling::*/*[. eq current()])
                  return 
                    if($cnt gt 0)
                      then concat($cnt, '_')
                      else (),
                  .
                 )
         "/>
      </name>
     </xsl:template>
    </xsl:stylesheet>
    

    when applied on the provided XML document:

    <XML>
        <Concept>
            <Heading-1>This is First Heading</Heading-1>
        </Concept>
        <Concept>
            <Heading-2>This is Second Heading</Heading-2>
        </Concept>
        <Concept>
            <Heading-2>This is First Heading</Heading-2>
        </Concept>
     </XML>
    

    produces the wanted, correct result:

    <name>This is First Heading</name>
    <name>This is Second Heading</name>
    <name>1_This is First Heading</name>
    

    In case you need each of the text nodes produced in the result to be actually in variables, then simply replace in the above code <xsl:sequence> with <xsl:variable>.