I have an xml like:
<PersonList>
<Person>
<Name>Smith</Name>
<Role>5</Role>
</Person>
<Person>
<Name>Star</Name>
<Role>3</Role>
</Person>
<Person>
<Name>Wars</Name>
<Role>1</Role>
</Person>
</PersonList>
In xslt I want to sort in such a way that if there is a Role 1 this should be the first Person in the list.
The rest of the Persons should be sorted alphabetically by Name.
Thanks in advance.
Try it this way:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/PersonList">
<xsl:copy>
<xsl:apply-templates select="Person">
<xsl:sort select="number(Role=1)" data-type="number" order="descending"/>
<xsl:sort select="Name" data-type="text" order="ascending"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Or, if you prefer:
...
<xsl:template match="/PersonList">
<xsl:copy>
<xsl:apply-templates select="Person[Role=1]"/>
<xsl:apply-templates select="Person[not(Role=1)]">
<xsl:sort select="Name" data-type="text" order="ascending"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
...
Additional Explanation:
The expression Role=1
returns a Boolean result. However, XSLT 1.0's xsl:sort
instruction can only sort by text or by number. Therefore the result must be converted to either a string or a number, before it can be used for sorting.
I prefer to convert the Boolean to a number, because then the intended order is easier to understand when reading the code.
For the same reason, I prefer to state the data-type
and order
explicitly, even when they are the default values of "text" and "ascending" respectively.
In any case, if one prefers to sort as text, the required process is the same in terms of complexity1:
<xsl:sort select="not(Role=1)"/>
is just a short-hand version of:
<xsl:sort select="string(not(Role=1))" data-type="text" order="ascending"/>
which is the same thing as:
<xsl:sort select="string(Role=1)" data-type="text" order="descending"/>
The only difference between these three is in code readability.
(1) One could argue that sorting numerically will be more efficient than sorting alphabetically, but I don't know that for a fact, so I won't.
Note:
These differences are minor and largely a matter of personal preference.
A simpler and shorter solution -- no numeric sorting is really necessary:
<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:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="*">
<xsl:sort select="not(Role=1)"/>
<xsl:sort select="Name"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Explanation:
When Boolean values are sorted, false()
comes before true()
.
Multi-key sorting