Reputation: 5986
Does anyone know if it's possible to loop through a template and pull out node values based on an iterating number. So for example, I have the following XML strucutre:
<nodes>
<node>
<label1>Label a</label1>
<value1>Value a</value1>
<label2>Label b</label2>
<value2>Value b</value2>
<label3>Label c</label3>
<value3>Value c</value3>
etc...
</node>
</nodes>
There are always 20 label/value pairs of data. I want to output these via XSLT in a table. By looping through a template 20 times (unless there's a better way).
The code I have below works, but it won't accept a dynamic number when outputting the values (e.g.
<xsl:value-of select="$node/label$index"/>
)
Here's the code so far:
<xsl:param name="currentPage"/>
<xsl:variable name="numberOfPairs" select="20" />
<xsl:template match="/">
<table>
<xsl:call-template name="outputData">
<xsl:with-param name="node" select="$currentPage" />
</xsl:call-template>
</table>
</xsl:template>
<xsl:template name="outputData">
<xsl:param name="node" select="." />
<xsl:param name="index" select="1" />
<tr>
<td><xsl:value-of select="$node/label1"/></td>
<td><xsl:value-of select="$node/value1"/></td>
</tr>
<xsl:if test="$index <= $numberOfPairs">
<xsl:call-template name="outputData">
<xsl:with-param name="node" select="$node" />
<xsl:with-param name="index" select="$index + 1" />
</xsl:call-template>
</xsl:if>
</xsl:template>
Can anyone suggest a solution to this?
Upvotes: 4
Views: 6045
Reputation: 243449
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pLimit" select="20"/>
<xsl:template match="/">
<xsl:apply-templates select=
"/*/*/*[starts-with(name(), 'label')
and
not(substring-after(name(), 'label') > $pLimit)
]"/>
</xsl:template>
<xsl:template match="*[starts-with(name(), 'label')]">
<tr>
<td><xsl:value-of select="."/></td>
<td><xsl:value-of select="following-sibling::*[1]"/></td>
</tr>
</xsl:template>
</xsl:stylesheet>
when applied on this XML document (similar to the provided, but with 21 label-value pairs):
<nodes>
<node>
<label1>Label a</label1>
<value1>Value a</value1>
<label2>Label b</label2>
<value2>Value b</value2>
<label3>Label c</label3>
<value3>Value c</value3>
<label4>Label d</label4>
<value4>Value d</value4>
<label5>Label e</label5>
<value5>Value e</value5>
<label6>Label f</label6>
<value6>Value f</value6>
<label7>Label g</label7>
<value7>Value g</value7>
<label8>Label h</label8>
<value8>Value h</value8>
<label9>Label i</label9>
<value9>Value i</value9>
<label10>Label j</label10>
<value10>Value j</value10>
<label11>Label k</label11>
<value11>Value k</value11>
<label12>Label l</label12>
<value12>Value l</value12>
<label13>Label m</label13>
<value13>Value m</value13>
<label14>Label n</label14>
<value14>Value n</value14>
<label15>Label o</label15>
<value15>Value o</value15>
<label16>Label p</label16>
<value16>Value p</value16>
<label17>Label q</label17>
<value17>Value q</value17>
<label18>Label r</label18>
<value18>Value r</value18>
<label19>Label s</label19>
<value19>Value s</value19>
<label20>Label t</label20>
<value20>Value t</value20>
<label21>Label u</label21>
<value21>Value u</value21>
</node>
</nodes>
produces the wanted, correct result:
<tr>
<td>Label a</td>
<td>Value a</td>
</tr>
<tr>
<td>Label b</td>
<td>Value b</td>
</tr>
<tr>
<td>Label c</td>
<td>Value c</td>
</tr>
<tr>
<td>Label d</td>
<td>Value d</td>
</tr>
<tr>
<td>Label e</td>
<td>Value e</td>
</tr>
<tr>
<td>Label f</td>
<td>Value f</td>
</tr>
<tr>
<td>Label g</td>
<td>Value g</td>
</tr>
<tr>
<td>Label h</td>
<td>Value h</td>
</tr>
<tr>
<td>Label i</td>
<td>Value i</td>
</tr>
<tr>
<td>Label j</td>
<td>Value j</td>
</tr>
<tr>
<td>Label k</td>
<td>Value k</td>
</tr>
<tr>
<td>Label l</td>
<td>Value l</td>
</tr>
<tr>
<td>Label m</td>
<td>Value m</td>
</tr>
<tr>
<td>Label n</td>
<td>Value n</td>
</tr>
<tr>
<td>Label o</td>
<td>Value o</td>
</tr>
<tr>
<td>Label p</td>
<td>Value p</td>
</tr>
<tr>
<td>Label q</td>
<td>Value q</td>
</tr>
<tr>
<td>Label r</td>
<td>Value r</td>
</tr>
<tr>
<td>Label s</td>
<td>Value s</td>
</tr>
<tr>
<td>Label t</td>
<td>Value t</td>
</tr>
Explanation:
Using the standard XPath functions name()
, starts-with()
and substring-after()
The maximum number of pairs to display is provided in the global (external) parameter named pLimit
.
The core of the solution is applying templates exactly on the set of Labelxx
elements that we want to display. These are any elements at depth 3 whose name starts with the string "label"
and where the remaining part of the name that follows the starting string "label"
is a number that is not greater than the specified limit $pLimit
.
Upvotes: 2
Reputation: 56162
<xsl:output method="xml" indent="yes" />
<xsl:template match="/">
<table>
<xsl:apply-templates select="nodes/node/*[starts-with(name(), 'label')]"/>
</table>
</xsl:template>
<xsl:template match="*">
<xsl:variable name="index" select="substring(name(), 6)"/>
<tr>
<td>
<xsl:value-of select="."/>
</td>
<td>
<xsl:value-of select="following-sibling::*[name()
= concat('value', $index)]"/>
</td>
</tr>
</xsl:template>
Output:
<table>
<tr>
<td>Label a</td>
<td>Value a</td>
</tr>
<tr>
<td>Label b</td>
<td>Value b</td>
</tr>
<tr>
<td>Label c</td>
<td>Value c</td>
</tr>
</table>
Upvotes: 3