Given the following input:
XML
<root>
<tfLink01>Link #2</tfLink01>
<mOrder>2</mOrder>
<tfText01 />
<cfSM1>false</cfSM1>
<tf1SLink01 />
<tf1SText01 />
<tf2SLink01 />
<tf2SText01 />
<tf3SLink01 />
<tf3SText01 />
<tfLink02>Link #1</tfLink02>
<mOrder>1</mOrder>
<tfText02 />
<cfSM2>false</cfSM2>
<tf1SLink02 />
<tf1SText02 />
<tf2SLink02 />
<tf2SText02 />
<tf3SLink02 />
<tf3SText02 />
<tfLink03>Link #3</tfLink03>
<mOrder>3</mOrder>
<tfText03 />
<cfSM3>false</cfSM3>
<tf1SLink03 />
<tf1SText03 />
<tf2SLink03 />
<tf2SText03 />
<tf3SLink03 />
<tf3SText03 />
</root>
the following stylehseet:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:template match="/root">
<ul>
<xsl:for-each select="mOrder">
<xsl:sort/>
<li>
<xsl:value-of select="preceding-sibling::*[1]"/>
</li>
</xsl:for-each>
</ul>
</xsl:template>
</xsl:stylesheet>
will return:
<ul>
<li>Link #1</li>
<li>Link #2</li>
<li>Link #3</li>
</ul>
Note:
You can capture the ordinal number in a variable, for example:
<xsl:variable name="ordinal" select="substring-after(name(preceding-sibling::*[1]), 'tfLink')" />
and use it to address additional nodes by their name, instead of by their position in the "block".
For an input where the mOrder
nodes are also numbered, i.e.:
XML
<root>
<tfLink01>Link #2</tfLink01>
<mOrder1>2</mOrder1>
<tfText01 />
<cfSM1>false</cfSM1>
<tf1SLink01 />
<tf1SText01 />
<tf2SLink01 />
<tf2SText01 />
<tf3SLink01 />
<tf3SText01 />
<tfLink02>Link #1</tfLink02>
<mOrder2>1</mOrder2>
<tfText02 />
<cfSM2>false</cfSM2>
<tf1SLink02 />
<tf1SText02 />
<tf2SLink02 />
<tf2SText02 />
<tf3SLink02 />
<tf3SText02 />
<tfLink03>Link #3</tfLink03>
<mOrder3>3</mOrder3>
<tfText03 />
<cfSM3>false</cfSM3>
<tf1SLink03 />
<tf1SText03 />
<tf2SLink03 />
<tf2SText03 />
<tf3SLink03 />
<tf3SText03 />
</root>
you can use:
<xsl:template match="/root">
<ul>
<xsl:for-each select="*[starts-with(name(), 'tfLink')]">
<xsl:sort select="following-sibling::*[1]"/>
<li>
<xsl:value-of select="."/>
</li>
</xsl:for-each>
</ul>
</xsl:template>
It is better if you structure your XML like this (omitted unused fields for brevity):
<?xml version="1.0" encoding="UTF-8"?>
<root>
<grp>
<tfLink>Link #2</tfLink>
<mOrder>2</mOrder>
</grp>
<grp>
<tfLink>Link #1</tfLink>
<mOrder>1</mOrder>
</grp>
<grp>
<tfLink>Link #3</tfLink>
<mOrder>3</mOrder>
</grp>
</root>
Your XSLT then becomes:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/root">
<div class="hidOverflow vertAlignT setCenter smallPadBottom">
<div id="dvExpProvHolder" class="hidOverflow innerDivCenter">
<ul class="uSPStyle" id="uSPStyle">
<xsl:for-each select="grp">
<xsl:sort select="mOrder" />
<li>
<a class="tfLink clickMe" title="{tfLink}" href="javascript:void(0);"
data-toggle='collapse' data-target="#{generate-id()}">
<xsl:value-of select="tfLink" />
</a>
</li>
</xsl:for-each>
</ul>
</div>
</div>
</xsl:template>
</xsl:stylesheet>
I've also modified the data-toggle
(I assume it's Bootstrap) to name the class it should toggle (collapse
as an example), and the data-target
to use generate-id()
, which you should use when generating the content to be acted upon.
The XML resulting from the above is this:
<?xml version="1.0"?>
<div class="hidOverflow vertAlignT setCenter smallPadBottom">
<div id="dvExpProvHolder" class="hidOverflow innerDivCenter">
<ul class="uSPStyle" id="uSPStyle">
<li>
<a class="tfLink clickMe" title="Link #1" data-toggle="collapse" data-target="#idp31396560" href="javascript:void(0);">Link #1</a>
</li>
<li>
<a class="tfLink clickMe" title="Link #2" data-toggle="collapse" data-target="#idp31377232" href="javascript:void(0);">Link #2</a>
</li>
<li>
<a class="tfLink clickMe" title="Link #3" data-toggle="collapse" data-target="#idp31398000" href="javascript:void(0);">Link #3</a>
</li>
</ul>
</div>
</div>
If you need to set attributes on the first element in the xsl:for-each, you can use this in the XSLT:
<xsl:for-each ...>
<xsl:sort .../>
<li>
<xsl:if test="position()=1">
<xsl:attribute name="class">setCurrent</xsl:attribute>
</xsl:if>
and similar for the <a>
.