How can I change the xsl order for each?

I need to change the order in which my for-each is executed only if certain conditions are met.

This is what my XML looks like:

<OptionList> <Option name="My First Option" /> <Option name="My Second Option" /> </OptionList> 

However, in some cases, my XML may be like this:

 <OptionList> <Option /> <Option name="My Second Option" /> </OptionList> 

In my XSL, I do for everyone like this:

 <xsl:for-each select="//OptionList/Option"> {...} </xsl:for-each> 

I know that I can change the order of Option nodes using this line in mine for each:

 <xsl:sort select="position()" data-type="number" order="descending" /> 

The problem is that I want my order to be omitted only when my first variant node is empty and has no name attribute. Otherwise, I want to keep the default by default.

Any data on how I can achieve this? So far, everything I've tried turned out to be "variable out of scope" or an invalid use of xpath functions.

+4
source share
3 answers

You can use the hack to change the sort order based on the condition:

 <?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes"/> <xsl:template match="/"> <result> <xsl:for-each select="//OptionList/Option"> <xsl:sort data-type="number" order="ascending" select="position()*(-2*number(not(//OptionList/Option[1]/@name))+1)"/> <option> <xsl:value-of select="@name"/> </option> </xsl:for-each> </result> </xsl:template> </xsl:stylesheet> 

The hack is that number((true()) returns 1 and number(false()) returns 0. As a result, the expression

 -2 * number(not(//OptionList/Option[1]/@name)) + 1 

1 if the first option element has a name attribute and -1 otherwise. This is used as a factor to reverse the sort order.

+4
source

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.

+1
source

Check if the first parameter has the name node or not

 <xsl:if test="Option/[position()=1]/@name">sort here</xsl:test> 
0
source

All Articles