XSLT Sort
XML stores data in the order it was written. But the order you want to display data is often different — alphabetically by name, highest salary first, or most recent date at the top. The xsl:sort instruction reorders nodes before the transformation processes them, without changing the original XML file.
xsl:sort Syntax
<xsl:for-each select="nodes">
<xsl:sort select="XPath-expression" order="ascending|descending"
data-type="text|number" case-order="upper-first|lower-first"/>
<!-- template content -->
</xsl:for-each>
Place xsl:sort as the first child inside xsl:for-each or xsl:apply-templates.
xsl:sort Attributes
| Attribute | Values | Default |
|---|---|---|
| select | XPath expression | . (current node) |
| order | ascending, descending | ascending |
| data-type | text, number | text |
| case-order | upper-first, lower-first | upper-first |
| lang | language code | system default |
Example 1: Sort Alphabetically by Name
XML Source
<employees> <employee><name>Vikram Singh</name><salary>72000</salary></employee> <employee><name>Anita Desai</name><salary>88000</salary></employee> <employee><name>Rahul Gupta</name><salary>65000</salary></employee> <employee><name>Kavitha Nair</name><salary>91000</salary></employee> </employees>
XSLT — Sort by name ascending
<xsl:for-each select="employees/employee"> <xsl:sort select="name" order="ascending" data-type="text"/> <p><xsl:value-of select="name"/></p> </xsl:for-each>
Output
Anita Desai Kavitha Nair Rahul Gupta Vikram Singh
Example 2: Sort by Number — Highest Salary First
<xsl:for-each select="employees/employee"> <xsl:sort select="salary" order="descending" data-type="number"/> <p><xsl:value-of select="name"/> — <xsl:value-of select="salary"/></p> </xsl:for-each>
Output
Kavitha Nair — 91000 Anita Desai — 88000 Vikram Singh — 72000 Rahul Gupta — 65000
Always set data-type="number" when sorting numeric values. Without it, XSLT sorts numbers as text — so 9 sorts after 80 because "9" > "8" alphabetically.
text vs number Sorting — Why It Matters
Values: 10, 9, 100, 20 Sorted as text (wrong for numbers): 10, 100, 20, 9 (compares character by character: "1" < "2" < "9") Sorted as number (correct): 9, 10, 20, 100
Example 3: Sort by Attribute
XML Source
<products> <product sku="P003"><name>Mouse</name><price>499</price></product> <product sku="P001"><name>Keyboard</name><price>1199</price></product> <product sku="P002"><name>Monitor</name><price>8999</price></product> </products>
Sort by SKU attribute
<xsl:for-each select="products/product"> <xsl:sort select="@sku" order="ascending" data-type="text"/> <p><xsl:value-of select="@sku"/> — <xsl:value-of select="name"/></p> </xsl:for-each>
Output
P001 — Keyboard P002 — Monitor P003 — Mouse
Multiple Sort Keys
Use multiple xsl:sort elements in sequence. The first xsl:sort is the primary key; subsequent ones are used as tiebreakers.
XML Source
<students> <student><class>10A</class><name>Rohit Sharma</name><marks>85</marks></student> <student><class>10B</class><name>Aisha Khan</name><marks>92</marks></student> <student><class>10A</class><name>Deepak Jain</name><marks>78</marks></student> <student><class>10B</class><name>Priya Mehta</name><marks>88</marks></student> </students>
Sort by class first, then by marks descending
<xsl:for-each select="students/student"> <xsl:sort select="class" order="ascending" data-type="text"/> <xsl:sort select="marks" order="descending" data-type="number"/> <p><xsl:value-of select="class"/> | <xsl:value-of select="name"/> | <xsl:value-of select="marks"/></p> </xsl:for-each>
Output
10A | Rohit Sharma | 85 10A | Deepak Jain | 78 10B | Aisha Khan | 92 10B | Priya Mehta | 88
Students group by class (10A before 10B). Within each class, higher marks appear first.
Sorting with xsl:apply-templates
xsl:sort works inside xsl:apply-templates too.
<xsl:apply-templates select="employees/employee"> <xsl:sort select="name" order="ascending"/> </xsl:apply-templates>
Key Points to Remember
- xsl:sort reorders nodes before transformation without altering the source XML.
- Place xsl:sort as the first child inside xsl:for-each or xsl:apply-templates.
- order="ascending" sorts A-Z or smallest first; order="descending" sorts Z-A or largest first.
- Always set data-type="number" when sorting numeric values.
- Multiple xsl:sort elements create primary and secondary sort keys.
- Sort by attribute values using @attributeName in the select expression.
Sorting by Date
Sorting dates works correctly only when dates follow the ISO format YYYY-MM-DD. This format sorts correctly as text because the year comes first, then month, then day — the same order as alphabetical and numeric sorting.
<xsl:for-each select="orders/order">
<xsl:sort select="orderDate" order="descending" data-type="text"/>
<p>
<xsl:value-of select="orderDate"/> —
<xsl:value-of select="customer"/>
</p>
</xsl:for-each>
Why ISO Date Format Sorts Correctly
Dates in ISO format: Text sort result (correct): 2024-03-15 2024-12-01 ← most recent first (descending) 2024-12-01 2024-03-15 2023-07-22 2023-07-22 ← oldest last Dates in DD/MM/YYYY: Text sort result (WRONG): 15/03/2024 22/07/2023 ← wrong — 2 sorts before 1 22/07/2023 01/12/2024
Always store dates in XML as YYYY-MM-DD to ensure correct sorting and comparison.
Sorting Strings with Non-ASCII Characters
For languages other than English, the lang attribute on xsl:sort tells the processor which language rules to use for alphabetical ordering. Different languages have different ordering rules for accented characters.
<xsl:sort select="name" lang="fr" order="ascending"/>
XSLT 1.0 processors may not fully support all language sort orders. For precise language-aware sorting in non-English content, use an XSLT 2.0 or 3.0 processor like Saxon.
Stable Sort Behavior
XSLT sorting is stable — when two nodes have the same value for the sort key, they keep their original document order relative to each other. You do not need to add a tiebreaker to preserve the original order of equal items.
<!-- These two students have the same class; their relative order
within the same class is preserved from the XML document order -->
<xsl:sort select="class" order="ascending"/>
Sorting Numbers Stored as Text
XML elements always store content as text. If a price element contains "1200", it is a string. Without data-type="number", xsl:sort compares characters — "9" sorts after "1200" because "9" is greater than "1" as a character.
<!-- WRONG: treats price as text --> <xsl:sort select="price"/> <!-- Result: 100, 1200, 200, 50, 9 (text order) --> <!-- CORRECT: treats price as a number --> <xsl:sort select="price" data-type="number"/> <!-- Result: 9, 50, 100, 200, 1200 (numeric order) -->
Practical Summary: Sorting Cheat Sheet
| What to sort | data-type | order |
|---|---|---|
| Names alphabetically A-Z | text | ascending |
| Names alphabetically Z-A | text | descending |
| Prices lowest first | number | ascending |
| Prices highest first | number | descending |
| Dates newest first | text | descending (ISO format only) |
| Dates oldest first | text | ascending (ISO format only) |
