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

AttributeValuesDefault
selectXPath expression. (current node)
orderascending, descendingascending
data-typetext, numbertext
case-orderupper-first, lower-firstupper-first
langlanguage codesystem 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 sortdata-typeorder
Names alphabetically A-Ztextascending
Names alphabetically Z-Atextdescending
Prices lowest firstnumberascending
Prices highest firstnumberdescending
Dates newest firsttextdescending (ISO format only)
Dates oldest firsttextascending (ISO format only)