views:

579

answers:

3

I have created a recursive template for getting the first n number of items from my XML.

It uses an index(counter) just like how I would in a for loop. Now how can I get a node from my XML using the index?

I have tried [position()=$index] but it had weird behaviour when trying to get deeper nodes in the XML hierarchy.

If I have XML such as:

<0>
 <1>
  <2>item</2>
  <2>item</2>
  <2>item</2>
  <2>item</2>
  <2>item</2>
  <2>item</2>
 </1>
</0>

I want to be able to count through and copy the 2's until I have as many as I want.

A: 

I would use <xsl:for-each ... > along with position() instead of recursion.

Jim Garrison
Apologies - blindly gave the same answer as you without paying attention.
Nic Gibson
A: 

I don't quite see why you need a recursive template to do this. You could use an <xsl:for-each> to achieve this. (I changed the element name to make it legal) For example:

<xsl:variable name='n' select='10'/>
<xsl:for-each select='two[position() &lt; $n]'>
    <!-- do whatever you need to do -->
</xsl:for-each>

You can use an select attribute you want and you could include an <xsl:sort> in the for-each if your input is more complex than your example.

Nic Gibson
Sorry maybe I should have added another detail. What I really want is to output the items in groups, I want to summarise the groups before they are output. But I need to group them first into a fragment tree so I can do functions on each group. This is what the loop I have does. So I just need to get the appropriate node according to the index. Or is this not possible? I'm a noob.
Ben
Are you using XSLT version 1 or 2? XSLT 2.0 will make that much easier I suspect - there are grouping statements in XSLT 2.0 where you need to roll your own in 1.0
Nic Gibson
+1  A: 

You say you want to process your elements in groups of n. The following XSLT 1.0 solution does it:

<xsl:stylesheet 
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
  <xsl:param name="pGroupCount" select="3" />

  <xsl:template match="/lvl-0">
    <xsl:copy>
      <!-- select the nodes that start a group -->
      <xsl:apply-templates mode="group" select="
        lvl-1/lvl-2[position() mod $pGroupCount = 1]
      " />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="lvl-2" mode="group">
    <!-- select the nodes belong to the current group -->
    <xsl:variable name="current-group" select="
      . | following-sibling::lvl-2[position() &lt; $pGroupCount]
    " />
    <!-- output the current group, you can also do calculations with it -->
    <group id="{position()}">
      <xsl:copy-of select="$current-group" />
    </group>
  </xsl:template>
</xsl:stylesheet>

When applied to this input document:

<lvl-0>
 <lvl-1>
  <lvl-2>item.0</lvl-2>
  <lvl-2>item.1</lvl-2>
  <lvl-2>item.2</lvl-2>
  <lvl-2>item.3</lvl-2>
  <lvl-2>item.4</lvl-2>
  <lvl-2>item.5</lvl-2>
  <a>foo</a><!-- to prove that position() calculations still work -->
  <lvl-2>item.6</lvl-2>
  <lvl-2>item.7</lvl-2>
  <lvl-2>item.8</lvl-2>
  <lvl-2>item.9</lvl-2>
 </lvl-1>
</lvl-0>

The following output is generated:

<lvl-0>
  <group id="1">
    <lvl-2>item.0</lvl-2>
    <lvl-2>item.1</lvl-2>
    <lvl-2>item.2</lvl-2>
  </group>
  <group id="2">
    <lvl-2>item.3</lvl-2>
    <lvl-2>item.4</lvl-2>
    <lvl-2>item.5</lvl-2>
  </group>
  <group id="3">
    <lvl-2>item.6</lvl-2>
    <lvl-2>item.7</lvl-2>
    <lvl-2>item.8</lvl-2>
  </group>
  <group id="4">
    <lvl-2>item.9</lvl-2>
  </group>
</lvl-0>

To understand this you must know how position() works. When used like this:

lvl-1/lvl-2[position() mod $pGroupCount = 1]

it refers to the position of lvl-2 nodes within their respective (!) parent. In this case, there is only one parent, so item.0 has position 1 and item.9 has position 10.

When used like this:

following-sibling::lvl-2[position() &lt; $pGroupCount]

it refers to the relative position along the following-sibling:: axis. In this context, item.1 would have a relative position of 1 in regard to item.0. (Basically, this is the same as the above, which just counts along the (implicit) child:: axis.)

When used on its own, like here:

<group id="{position()}">

it refers to the position of the current node in the batch that is being processed at the moment. In our case the "batch" consists of nodes that start a group (item.0, item.3, item.6, item.9), so it goes from 1 to 4.

Tomalak