Search code examples
xsltposition

how to renumbering positions in xslt


I would like to ask for any idea how to renumbering LINENUM and references in PARENTID by XSLT. LINENUM should be in correct sequence. Currently there is 1,4,5 because there was used position() which is not correct. LINENUM could be blank if it is child and has refence number in PARENTID. If LINENUM is blank it should not be calculated in sequence. Also there could be only parent record without child. There is simple xml example.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<data>
      <line>
         <LINENUM>1</LINENUM>
         <PARENTID/>
         <IDACTION>240730180714572</IDACTION>
      </line>
      <line>
         <LINENUM/>
         <PARENTID>1</PARENTID>
         <IDACTION></IDACTION>
      </line> 
      <line>
         <LINENUM/>
         <PARENTID>1</PARENTID>
         <IDACTION>240730180714573</IDACTION>
      </line>
      <line>
         <LINENUM>4</LINENUM>
         <PARENTID></PARENTID>
         <IDACTION>240730180714575</IDACTION>
      </line>     
      <line>
         <LINENUM>5</LINENUM>
         <PARENTID/>
         <IDACTION>240730180714578</IDACTION>
      </line>
      <line>
         <LINENUM/>
         <PARENTID>5</PARENTID>
         <IDACTION>240730180714579</IDACTION>
      </line>
      <line>
         <LINENUM/>
         <PARENTID>5</PARENTID>
         <IDACTION>2407301807145712</IDACTION>
      </line>
</data>

Expected result is below.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<data>
      <line>
         <LINENUM>1</LINENUM>
         <PARENTID/>
         <IDACTION>240730180714572</IDACTION>
      </line>
      <line>
         <LINENUM/>
         <PARENTID>1</PARENTID>
         <IDACTION></IDACTION>
      </line>
      <line>
         <LINENUM/>
         <PARENTID>1</PARENTID>
         <IDACTION>240730180714573</IDACTION>
      </line>
      <line>
         <LINENUM>2</LINENUM>
         <PARENTID></PARENTID>
         <IDACTION>240730180714575</IDACTION>
      </line>     
      <line>
         <LINENUM>3</LINENUM>
         <PARENTID/>
         <IDACTION>240730180714578</IDACTION>
      </line>
      <line>
         <LINENUM/>
         <PARENTID>3</PARENTID>
         <IDACTION>240730180714579</IDACTION>
      </line>
      <line>
         <LINENUM/>
         <PARENTID>3</PARENTID>
         <IDACTION>2407301807145712</IDACTION>
      </line>
</data>

Solution

  • The outer loop processes just the parent <line> elements (those which have text in their <LINENUM> child element), and copies it, renumbering it based on its position in the sequence. The inner loop then copies all the subordinate <line> elements that match the current parent <line>. Here I've used an xsl:key to allow the subordinate <line> elements to be looked up using their existing <PARENTID>.

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
    <xsl:output method="xml" indent="true"/>
    
    <xsl:key name="line-by-PARENTID" match="line" use="PARENTID"/>
    
    <xsl:template match="/data">
      <xsl:copy>
        <xsl:for-each select="line[LINENUM/text()]">
          <!-- the line's new number is its position in the 
               sequence of lines which have a LINENUM value -->
          <xsl:variable name="new-number" select="position()"/>
          <xsl:copy>
            <!-- update the LINENUM with the new number -->
            <LINENUM><xsl:value-of select="$new-number"/></LINENUM>
            <PARENTID/>
            <xsl:copy-of select="IDACTION"/>
          </xsl:copy>
          <!-- copy and renumber all the lines which have the 
               current line's LINENUM as their PARENTID value -->
          <xsl:for-each select="key('line-by-PARENTID', LINENUM)">
            <xsl:copy>
              <LINENUM/>
              <PARENTID><xsl:value-of select="$new-number"/></PARENTID>
              <xsl:copy-of select="IDACTION"/>
            </xsl:copy>
          </xsl:for-each>
        </xsl:for-each>
      </xsl:copy>
    </xsl:template>
    
    </xsl:stylesheet>