Reputation: 291
Can someone please explain me why I get the following output applying the follwoing xsl-file to the xml-file.
<?xml version="1.0" encoding="ISO-8859-1"?>
<source>
<number>1</number>
<number>2</number>
<number>3</number>
<number>4</number>
<number>5</number>
<number>6</number>
<number>7</number>
<number>8</number>
</source>
====================================
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version = '1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<xsl:template match="number">
<p>
<xsl:value-of select="position()"/>
<xsl:text> of </xsl:text>
<xsl:value-of select="last()"/>
</p>
</xsl:template>
</xsl:stylesheet>
======================================
<p>2 of 17</p>
<p>4 of 17</p>
<p>6 of 17</p>
<p>8 of 17</p>
<p>10 of 17</p>
<p>12 of 17</p>
<p>14 of 17</p>
<p>16 of 17</p>
I don't quite get why the output is not 1 of 8, 2 of 8 and so on.
Upvotes: 5
Views: 1272
Reputation: 52888
Another option is to use xsl:number. It's much more powerful than position()
. (The additional capabilities of xsl:number
aren't needed in this simple example, but it is good to know.)
Example:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:template match="number">
<p>
<xsl:number/>
<xsl:text> of </xsl:text>
<xsl:value-of select="../number[last()]"/>
</p>
</xsl:template>
</xsl:stylesheet>
Upvotes: 0
Reputation: 22647
A good answer has already been given by @ColinE. I would like to add another way of bypassing this problem of counting nodes that are not explicitly targeted by your templates. I.e. without using strip-space
.
Matching your document node (source) and using an xsl:for-each
element, you make sure that only number
elements are taken into consideration. In other words, the superfluous nodes are now out of context.
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version = '1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<xsl:output method="xml" omit-xml-declaration="yes"/>
<xsl:template match="source">
<xsl:for-each select="number">
<p>
<xsl:value-of select="position()"/>
<xsl:text> of </xsl:text>
<xsl:value-of select="last()"/>
</p>
<xsl:text> </xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
This gives the same output:
<p>1 of 8</p>
<p>2 of 8</p>
<p>3 of 8</p>
<p>4 of 8</p>
<p>5 of 8</p>
<p>6 of 8</p>
<p>7 of 8</p>
<p>8 of 8</p>
On the other hand, strip-space
makes your input look like this:
<source><number>1</number><number>2</number><number>3</number><number>4</number><number>5</number><number>6</number><number>7</number><number>8</number></source>
Now, if you apply your initial stylesheet to the above input XML that lacks any whitespace, you will arrive at the same result as when using the solutions put forward by ColinE and me.
Upvotes: 4
Reputation: 70160
Try adding strip-space as shown below:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version = '1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<xsl:strip-space elements="*"/>
<xsl:template match="number">
<p>
<xsl:value-of select="position()"/>
<xsl:text> of </xsl:text>
<xsl:value-of select="last()"/>
</p>
</xsl:template>
</xsl:stylesheet>
This gives the following output:
<p>1 of 8</p>
<p>2 of 8</p>
<p>3 of 8</p>
<p>4 of 8</p>
<p>5 of 8</p>
<p>6 of 8</p>
<p>7 of 8</p>
<p>8 of 8</p>
This is due to whitespace issues, as described in this document. Basically the nodeset contains whitespace nodes that are not matched by your template, but contribute to the index of each node.
Upvotes: 4