简体   繁体   中英

Count nodes based on the sort function of for-each loop in xslt 1.0

The input xml has following hierarchy.

<a>  
 <b>
  <b1>3</b1> 
  <c> 
    <d>
    <d1>13</d1>
    <d2>text1</d2> 
    </d>
    <d>
    <d1>14</d1>
    <d2>text2</d2>
    </d>   
    <d>
    <d1>12</d1>
    <d2>text3</d2> 
    </d>
  </c>  
 </b>  
 <b>
  <b1>2</b1> 
  <c>  
    <d> 
    <d1>11</d1>
    <d2>text5</d2>
    </d>
    <d>
    <d1>10</d1>
    <d2>text4</d2>
    </d>
  </c>  
 </b> 
  ---- and so on.
</a>

I have a xsl template where part of it looks like

    <xsl:for-each select="a/b">
    <xsl:sort select="b1" data-type="number"/>
    ----- other mapping---
     <xsl:for-each select="c/d">
     <xsl:sort select="d1" data-type="number"/>
       <xsl:variable name="cnt">
       <xsl:value-of select="count(preceding::../../c/d)"/>
      </xsl:variable>
     ------ other mapping------
     </xsl:for-each>
    </xsl:for-each>

Now even after applying sort function, the count function is following the order of 'd' in the input xml and not based on sorted ascending order of 'd' in the for-each statement. for-eg In loop 2 of element b ie b[position()=2] i am getting cnt = 3 though it should be 2. Please advice.

The preceding:: axis only looks at nodes in document order - the order that they really are in your input. It doesn't take anything like the current sequence or the current sort order into account.

If you really want to count how many <d> elements there are before the current in, in the sort order, then you can do that knowing that each element before it should have a lower value for the sort key than the current one:

<xsl:variable name="d1" select="number(d1)" />
<xsl:value-of select="count(../d[number(d1) &lt; $d1])" />

But there already is a function in xslt that will give you the position of the current node in the sort order of the for-each loop, which is the position() function. It starts to count at 1 so you need to subtract one if you want to know how many nodes are before the current one:

<xsl:for-each select="a/b">
    <xsl:sort select="b1" data-type="number" />
    ----- other mapping---
    <xsl:for-each select="c/d">
        <xsl:sort select="d1" data-type="number" />
        <xsl:variable name="cnt">
            <xsl:value-of select="position() - 1" />
        </xsl:variable>
        ------ other mapping------
    </xsl:for-each>
</xsl:for-each>

This is from the xslt 1.1 specification, which describes it better than the xslt 1.0 specification, but it is also true in xslt 1.0:

[The context position is the position of the context item within the sequence of items currently being processed. It changes whenever the context item changes. When an instruction such as xsl:apply-templates or xsl:for-each is used to process a sequence of items, the first item in the sequence is processed with a context position of 1, the second item with a context position of 2, and so on.] The context position is returned by the XPath expression position() .

From your further comment it looks like you want a number that represents the overall order of the current node in the two for loops, in the sort order of the for loops.

You can use my first approach: to count the nodes that are before this one in the sort order by comparing the values using '<'.

Then your XSL becomes:

<xsl:for-each select="a/b">
    <xsl:sort select="b1" data-type="number" />
    ----- other mapping---
    <xsl:for-each select="c/d">
        <xsl:sort select="d1" data-type="number" />
        <xsl:variable name="me" select="." />
        <xsl:variable name="cnt">
            <xsl:value-of select="count(../d[number(d1) &lt; number($me/d1)]) + count(../../../b/c/d[number(../../b1) &lt; number($me/../../b1)])" />
        </xsl:variable>
        ------ other mapping------
    </xsl:for-each>
</xsl:for-each>

I think this is what you describe. If you still want something else, then I think your question is not very clear - given your sample input file, please describe exactly what result you are looking for in which order.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM