How to Define Multiple Sort Keys in XSLT?
Sorting XML nodes by multiple criteria is a common requirement in
XSLT transformations, allowing developers to create primary, secondary,
and tertiary sorting orders. In XSLT, multiple sort keys are defined by
placing consecutive xsl:sort elements within an
xsl:for-each block or an xsl:apply-templates
call. The XSLT processor evaluates these elements in the exact order
they appear, meaning the first element defines the primary sort key, the
second defines the secondary sort key, and each subsequent element
handles further tie-breaking.
Understanding the xsl:sort Hierarchy
The xsl:sort instruction does not require a complex
multi-column expression. Instead, the sequence of the
xsl:sort elements dictates precedence:
- Primary Sort Key: The first
xsl:sortchild element determines the base ordering of the node-set. - Secondary Sort Key: The second
xsl:sortchild element resolves any ties where nodes share identical values in the primary key. - Tertiary and Beyond: Additional
xsl:sortelements continue resolving subsequent ties in order.
Each xsl:sort element operates independently, allowing
unique configurations for data types, sorting directions, case ordering,
and language collations on a per-key basis.
Practical Example: Sorting Within xsl:for-each
Consider an XML document containing a list of employees with department, last name, and salary attributes:
<employees>
<employee dept="Sales" lastName="Smith" salary="60000"/>
<employee dept="Engineering" lastName="Doe" salary="85000"/>
<employee dept="Sales" lastName="Adams" salary="75000"/>
<employee dept="Engineering" lastName="Smith" salary="90000"/>
<employee dept="Sales" lastName="Smith" salary="65000"/>
</employees>To sort employees alphabetically by department (primary), then
alphabetically by last name (secondary), and finally by salary in
descending order (tertiary), place three xsl:sort elements
inside the xsl:for-each loop:
<xsl:template match="/employees">
<table border="1">
<tr>
<th>Department</th>
<th>Last Name</th>
<th>Salary</th>
</tr>
<xsl:for-each select="employee">
<!-- Primary Key: Department ascending (text) -->
<xsl:sort select="@dept" order="ascending" data-type="text"/>
<!-- Secondary Key: Last Name ascending (text) -->
<xsl:sort select="@lastName" order="ascending" data-type="text"/>
<!-- Tertiary Key: Salary descending (numeric) -->
<xsl:sort select="@salary" order="descending" data-type="number"/>
<tr>
<td><xsl:value-of select="@dept"/></td>
<td><xsl:value-of select="@lastName"/></td>
<td><xsl:value-of select="@salary"/></td>
</tr>
</xsl:for-each>
</table>
</xsl:template>Using Multiple Sort Keys with xsl:apply-templates
The exact same multi-key sorting mechanism applies when invoking
template rules via xsl:apply-templates. The
xsl:sort elements must appear as direct children of the
xsl:apply-templates element, placed before any
xsl:with-param elements:
<xsl:template match="/employees">
<xsl:apply-templates select="employee">
<xsl:sort select="@dept" order="ascending" data-type="text"/>
<xsl:sort select="@lastName" order="ascending" data-type="text"/>
<xsl:sort select="@salary" order="descending" data-type="number"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="employee">
<div>
<xsl:value-of select="concat(@dept, ' - ', @lastName, ': $', @salary)"/>
</div>
</xsl:template>Key Attributes for Fine-Tuning Sort Order
Customizing individual sort keys requires leveraging the standard
attributes available on the xsl:sort element:
select: An XPath expression specifying the string or node value to sort by. If omitted, the string-value of the context node (.) is used.order: Accepts either"ascending"(default) or"descending".data-type: Accepts"text"(default, for alphabetical sorting) or"number"(for numeric comparisons). Setting this to"number"ensures values like10sort after2, rather than before it.case-order: Specifies whether uppercase or lowercase characters take priority when sorting text ("upper-first"or"lower-first").lang: Specifies the language code (e.g.,lang="en",lang="sv") to apply correct locale-sensitive collation rules.
By combining multiple, distinct xsl:sort statements with
these attribute configurations, XSLT provides robust control over
complex, multi-tiered sorting scenarios.