How to get a counter inside xsl:for-each loop that would reflect the number of current element processed.
For example my source XML is
<books>
<book>
<title>The Unbearable Lightness of Being </title>
</book>
<book>
<title>Narcissus and Goldmund</title>
</book>
<book>
<title>Choke</title>
</book>
</books>
What I want to get is:
<newBooks>
<newBook>
<countNo>1</countNo>
<title>The Unbearable Lightness of Being </title>
</newBook>
<newBook>
<countNo>2</countNo>
<title>Narcissus and Goldmund</title>
</newBook>
<newBook>
<countNo>3</countNo>
<title>Choke</title>
</newBook>
</newBooks>
The XSLT to modify:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<newBooks>
<xsl:for-each select="books/book">
<newBook>
<countNo>???</countNo>
<title>
<xsl:value-of select="title"/>
</title>
</newBook>
</xsl:for-each>
</newBooks>
</xsl:template>
</xsl:stylesheet>
So the question is what to put in place of ???. Is there any standard keyword or do I simply must declare a variable and increment it inside the loop?
As the question is pretty long I should probably expect one line or one word answer :)
XSLT is a functional language, not a procedural language, so you can't declare a counter. You can use xsl:number to get the position of the current node in its parent, if that helps. You can coerce a string to a number by using the XPath number() function.
The <xsl:for-each> element selects a set of nodes and processes each of them in the same way. It is often used to iterate through a set of nodes or to change the current node. If one or more <xsl:sort> elements appear as the children of this element, sorting occurs before processing.
A CDATA element can be used to present formatted data or an XML document on its own. The problem is that the XSLT processor will normally strip any leading and trailing whitespace, as well as carriage returns from an output XML document – unless instructed otherwise…
Remarks. The <xsl:for-each> element establishes the context for iteration. The XSLT transformation instructions within this loop are to be applied to the selected nodes.
position()
. E.G.:
<countNo><xsl:value-of select="position()" /></countNo>
Try inserting <xsl:number format="1. "/><xsl:value-of select="."/><xsl:text>
in the place of ???.
Note the "1. " - this is the number format. More info: here
Try:
<xsl:value-of select="count(preceding-sibling::*) + 1" />
Edit - had a brain freeze there, position() is more straightforward!
You can also run conditional statements on the Postion() which can be really helpful in many scenarios.
for eg.
<xsl:if test="(position( )) = 1">
//Show header only once
</xsl:if>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<newBooks>
<xsl:for-each select="books/book">
<newBook>
<countNo><xsl:value-of select="position()"/></countNo>
<title>
<xsl:value-of select="title"/>
</title>
</newBook>
</xsl:for-each>
</newBooks>
</xsl:template>
</xsl:stylesheet>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With