user3629892
user3629892

Reputation: 3046

understanding position() function in XSLT

I have a similar problem as this guy:

using position() function in xslt

But I dont need the numbering, I just want to understand the way it works:

<?xml version="1.0" encoding="UTF-8"?>
<test>
<a>blah</a>
<a>blah</a>
<a>blah</a>
<a>blah</a>
</test>

for this input, the following stylesheet:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/">
  <html>
  <body>
<xsl:apply-templates/>
  </body>
  </html>
</xsl:template>

<xsl:template match="select">
<xsl:apply-templates/>
</xsl:template>

<xsl:template match="a">
<xsl:value-of select="position()"/><br/>
<xsl:apply-templates/>
</xsl:template>

</xsl:stylesheet>

outputs:

<html><body>
2<br>blah
4<br>blah
6<br>blah
8<br>blah
</body></html>

Why does it skip the uneven numbers?

Upvotes: 10

Views: 63811

Answers (2)

michael.hor257k
michael.hor257k

Reputation: 116993

Why does it skip the uneven numbers?

Because while you were at the / level, you said:

<xsl:apply-templates/>

which applies templates to all nodes children of the root node, and (due to the built-in template rules) to all of their descendants - including the text nodes that separate the <a> elements.

You will get a different result with an input of:

<?xml version="1.0" encoding="UTF-8"?>
<test><a>blah</a><a>blah</a><a>blah</a><a>blah</a></test>

or if you add an instruction to your stylesheet to:

<xsl:strip-space elements="*"/>

or if you apply templates selectively, e.g.

<xsl:apply-templates select="//a"/>

Upvotes: 9

Ian Roberts
Ian Roberts

Reputation: 122374

The position() function gives you the position of the current node within the "current node list", which is whatever was select-ed by the nearest apply-templates or for-each (XSLT 2.0 refers to "items" and "sequences" rather than nodes and node lists but the principle is the same).

In your example the root template applies templates to its two child nodes (the text node containing the line break after the xml declaration, and the test element). There's no explicit rule matching test so the default rule applies, which for elements means <xsl:apply-templates/>.

Thus when the a template fires its "current node list" is the set of all 9 child nodes of test. This list consists of alternating text nodes (the line breaks) and element nodes (the a elements), with the text nodes at the odd positions and the element nodes at the even positions.

If you added an explicit template for test like this:

<xsl:template match="test">
  <xsl:apply-templates select="*"/>
</xsl:template>

Then this would select only the 4 a element nodes as the current node list, so position() would give you 1, 2, 3 and 4.

Upvotes: 9

Related Questions