Reputation: 1310
lets say I have the following xml file:
<jobs>
<job>
<PositionTitle>Painter</PositionTitle>
<InternalOrExternal>External</InternalOrExternal>
</job>
<job>
<PositionTitle>Plumber</PositionTitle>
<InternalOrExternal>Internal</InternalOrExternal>
</job>
<job>
<PositionTitle>Chemist</PositionTitle>
<InternalOrExternal>Internal</InternalOrExternal>
</job>
<job>
<PositionTitle>Teacher</PositionTitle>
<InternalOrExternal>External</InternalOrExternal>
</job>
</jobs>
.
I process it with the following xslt to show only the external jobs:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:content="http://purl.org/rss/1.0/modules/content/" >
<xsl:output method="html"/>
<xsl:template match="/">
<xsl:for-each select="jobs/job">
<xsl:if test="InternalOrExternal = 'External'"><!-- Only show external jobs -->
<xsl:value-of select="PositionTitle"/> - <xsl:value-of select="position()"/><br />
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
This results as:
Painter - 1
Teacher - 4
I assume the position() function returns the actual position in the xml file and doesn't take into account any if statements that may exclude some jobs. In this case the Internal jobs are excluded but their position is still counted.
The result that I want is:
Painter - 1
Teacher - 2
Is their any way I can get the position() function to only count what I display?
I've tried this with no luck:
<xsl:value-of select="position(jobs/job[InternalOrExternal='External'])"/>
Upvotes: 1
Views: 1008
Reputation: 52858
In addition to Ian Roberts' great answer: If you had to have the context be job
(for whatever reason), you could also use xsl:number
instead of position()
.
Example:
<xsl:value-of select="PositionTitle"/> - <xsl:number count="job[InternalOrExternal='External']"/><br />
Upvotes: 0
Reputation: 122374
position()
gives you the position of the current node within the "current node list", i.e. whatever list of nodes were selected by the most recent for-each
or apply-templates
. You can think of this informally as the current iteration number of the for-each
1. By saying
<xsl:for-each select="jobs/job">
the current node list is all the job elements, so you get position 1 for the first one and position 4 for the fourth one. If you remove the xsl:if
and instead move the filtering into a predicate on the for-each
selector:
<xsl:for-each select="jobs/job[InternalOrExternal='External']">
<xsl:value-of select="PositionTitle"/> - <xsl:value-of select="position()"/><br />
</xsl:for-each>
then the current node list only includes the "External" job
elements, and you'll get the position()
values you require.
1 Since XSLT instructions don't have side effects (e.g. there are no updateable variables), the XSLT processor doesn't necessarily have to implement for-each
internally using a sequential loop. It could choose to process different nodes in parallel or out of order, as long as it assembles the output in such a way that it looks the same as a sequential loop would produce.
Upvotes: 4