I'm new to xslt but I had to tackle a problem involving sorting an xml structure based on attributes of generic children. Something like the xml below.
<data>
<node name="Node1">
<attribute name="sortValue" value="value1"/>
<attribute name="color" value="red"/>
<attribute name="size" value="XL"/>
</node>
<node name="Node2">
<attribute name="sortValue" value="value3"/>
<attribute name="color" value="pink"/>
<attribute name="size" value="L"/>
</node>
<node name="Node3">
<attribute name="sortValue" value="value2"/>
<attribute name="color" value="blue"/>
<attribute name="size" value="M"/>
</node>
<node name="Node4">
<attribute name="sortValue" value="value3"/>
<attribute name="color" value="black"/>
<attribute name="size" value="S"/>
</node>
</data>
After searching a little I found some solutions and documented them here for future reference.
Simple Sorting
The simple case would involve using the sortValue attributes. To perform a simple sort for the xml on the sort field you can use the following xslt:
<xsl:template match="/">
<html>
<body>
<table border="1">
<tr bgcolor="pink">
<th>Node Name</th>
<th>Sort Value</th>
<th>Color</th>
<th>Size</th>
</tr>
<xsl:for-each select="data/node">
<xsl:sort select="attribute/@value[../@name = 'sortValue']"/>
<tr>
<td><xsl:value-of select="@name"/></td>
<xsl:for-each select="attribute">
<td><xsl:value-of select="@value"/></td>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
This should give you the following result:
Node Name | Sort Value | Color | Size |
Node1 | value1 | red | XL |
Node3 | value2 | blue | M |
Node2 | value3 | pink | L |
Node4 | value3 | black | S |
Sorting by multiple attributes
XSLT also supports multiple sorting so if you want to sort by multiple attributes (for example the last two lines have the same sort value but the color are not sorted) you can add a second sort. For this exmpla just change
<xsl:sort select="attribute/@value[../@name = 'sortValue']"/>
with
<xsl:sort select="attribute/@value[../@name = 'sortValue']"/>
<xsl:sort select="attribute/@value[../@name = 'color']"/>
and you should get:
Node Name | Sort Value | Color | Size |
Node1 | value1 | red | XL |
Node3 | value2 | blue | M |
Node4 | value3 | black | S |
Node2 | value3 | pink | L |
Sorting finite options by priority
If you have a list of possibilities with a finite number of priorities (like the clothes sizes in the example) you can use a trick to prioritize the list. This involves converting the options to numbers and giving each a weight for the purpose of sorting and performing the sort on numeric values.
To sort by size in this example you could use the following:
<xsl:sort data-type='number' order='ascending'
select="(number(attribute/@value[../@name = 'size'] = 'S') * 1)
+ (number(attribute/@value[../@name = 'size'] = 'M') * 2)
+ (number(attribute/@value[../@name = 'size'] = 'L') * 3)
+ (number(attribute/@value[../@name = 'size'] = 'XL') * 4)"/>
This should give the following result:
Node Name | Sort Value | Color | Size |
Node4 | value3 | black | S |
Node3 | value2 | blue | M |
Node2 | value3 | pink | L |
Node1 | value1 | red | XL |
Happy sorting