The order of creation of the desired result can be easily set using <xsl:sort> :
It is natural and lightweight and contains almost no hacks.
<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:variable name="vOrder" select= "2*boolean(/*/Option[1]/@name)-1"/> <xsl:template match="node()|@*"> <xsl:copy> <xsl:apply-templates select="node()|@*"/> </xsl:copy> </xsl:template> <xsl:template match="/*"> <xsl:copy> <xsl:for-each select="Option"> <xsl:sort data-type="number" select="$vOrder* position()"/> <xsl:apply-templates select="."/> </xsl:for-each> </xsl:copy> </xsl:template> </xsl:stylesheet>
When this conversion is performed in this XML document :
<OptionList> <Option name="My First Option" /> <Option name="My Second Option" /> <Option name="My Third Option" /> </OptionList>
required, the correct result is obtained :
<OptionList> <Option name="My First Option"/> <Option name="My Second Option"/> <Option name="My Third Option"/> </OptionList>
If the same conversion is performed in this XML document :
<OptionList> <Option /> <Option name="My Second Option" /> <Option name="My Third Option" /> </OptionList>
the desired, correct result is created again :
<OptionList> <Option name="My Third Option"/> <Option name="My Second Option"/> <Option/> </OptionList>
Explanation : the $vOrder variable is defined in such a way that it is -1 if the first Option element does not have a name attribute, and this is +1 if the first Option element has a name attribute. Here we use the fact that false() automatically converts to 0 and true() to 1 .
We also use the fact that when the sign of each number in a sequence of increasing positive numbers (positions) is reversed, the order of the new sequence decreases.
source share