Reputation: 57
I am new to XSLT and I have an XML like so;
<TerminalRoutes>
<Segment>
<From>
<previous>DEN </previous>
<current>DNV</current>
</From>
<From>
<previous>LAX </previous>
<current>LAS</current>
</From>
</Segment>
<Segment>
<To>
<previous>ATL </previous>
<current>ATN</current>
</To>
<To>
<previous>JFK</previous>
<current>LGA </current>
</To>
</Segment>
</TerminalRoutes>
I am trying to transform the XML into HTML using XSLT and I am applying this template
<xsl:template match="TerminalRoutes">
<tr>
<td>
<b>Terminal Routes</b>
</td>
<td> </td>
<td> </td>
</tr>
<tr>
<td>TERMINAL ROUTES FROM</td>
<td>
<xsl:value-of select="Segment/From/previous"></xsl:value-of>
</td>
<td>
<xsl:value-of select="Segment/From/current"></xsl:value-of>
</td>
</tr>
<tr>
<td>TERMINAL ROUTES TO</td>
<td>
<xsl:value-of select="Segment/To/previous"></xsl:value-of>
</td>
<td>
<xsl:value-of select="Segment/To/current"></xsl:value-of>
</td>
</tr>
</xsl:template>
and the resultant HTML gives me the first result set i.e the first "From" and the first "To".
What I want is to re-apply the same template, so I can capture the second result set i.e the second "From" and the second "To". I believe there is a position()
function that can be used with but I am not sure how.
Upvotes: 1
Views: 6579
Reputation: 11416
Just as example for how position()
can be used in a for-each
loop:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:template match="TerminalRoutes">
<table>
<xsl:for-each select="Segment/From">
<xsl:variable name="position" select="position()"/>
<tr>
<td>
<b>Terminal Routes</b>
</td>
<td> </td>
<td> </td>
</tr>
<tr>
<td>TERMINAL ROUTES FROM</td>
<td>
<xsl:value-of select="previous"></xsl:value-of>
</td>
<td>
<xsl:value-of select="current"></xsl:value-of>
</td>
</tr>
<tr>
<td>TERMINAL ROUTES TO</td>
<td>
<xsl:value-of select="//Segment/To[$position]/previous"></xsl:value-of>
</td>
<td>
<xsl:value-of select="//Segment/To[$position]/current"></xsl:value-of>
</td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
Output:
<table>
<tr>
<td><b>Terminal Routes</b></td>
<td> </td>
<td> </td>
</tr>
<tr>
<td>TERMINAL ROUTES FROM</td>
<td>DEN </td>
<td>DNV</td>
</tr>
<tr>
<td>TERMINAL ROUTES TO</td>
<td>ATL </td>
<td>ATN</td>
</tr>
<tr>
<td><b>Terminal Routes</b></td>
<td> </td>
<td> </td>
</tr>
<tr>
<td>TERMINAL ROUTES FROM</td>
<td>LAX </td>
<td>LAS</td>
</tr>
<tr>
<td>TERMINAL ROUTES TO</td>
<td>JFK</td>
<td>LGA </td>
</tr>
</table>
position()
automatically increments in the for-each loop, so select="//Segment/To[$position]/previous"
will be //Segment/To[1]/previous
for the first Segment/From
and //Segment/To[2]/previous
for the second one.
For reference: http://msdn.microsoft.com/en-US/en-en/library/ms256233%28v=vs.110%29.aspx
Upvotes: 0
Reputation: 24430
Try this:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="msxsl"
>
<xsl:output method="html" encoding="UTF-8" indent="yes" />
<xsl:template match="TerminalRoutes">
<tr>
<td>
<b>Terminal Routes</b>
</td>
<td> </td>
<td> </td>
</tr>
<xsl:apply-templates select=".//*" />
</xsl:template>
<xsl:template match="/TerminalRoutes/Segment/From">
<tr>
<td>TERMINAL ROUTES FROM</td>
<td>
<xsl:value-of select="./previous"></xsl:value-of>
</td>
<td>
<xsl:value-of select="./current"></xsl:value-of>
</td>
</tr>
<tr>
<td>TERMINAL ROUTES TO</td>
<td>
<xsl:value-of select="../../Segment/To[1 + count(preceding-sibling::*)]/previous"></xsl:value-of>
</td>
<td>
<xsl:value-of select="../../Segment/To[1 + count(preceding-sibling::*)]/current"></xsl:value-of>
</td>
</tr>
</xsl:template>
<xsl:template match="*" />
</xsl:stylesheet>
1 + count(preceding-sibling::*)
gives you the index of the current FROM element compared to it's siblings by counting the number preceding it (NB: indexes start at 1 instead of 0 in XML technologies).
We then use this number to specify that we want the To
element with the same index as the From
element we're currently matching on.
NB: Despite the above solution working, if possible I'd recommend a better approach: restructure your source XML to better group the data; i.e. put related FROM and TO values under the same parent, so that the data structure implies this relationship, making it simpler to read, validate, and work with.
e.g.
<TerminalRoutes>
<Route>
<From>
<previous>DEN </previous>
<current>DNV</current>
</From>
<To>
<previous>ATL </previous>
<current>ATN</current>
</To>
</Route>
<Route>
<From>
<previous>LAX </previous>
<current>LAS</current>
</From>
<To>
<previous>JFK</previous>
<current>LGA </current>
</To>
</Route>
</TerminalRoutes>
Upvotes: 1