Sorting is a very common requirement in XSLT transformations. XSLT gives us the xsl:sort instruction for this. It works inside xsl:for-each and inside xsl:apply-templates. While working on integration scenarios, I have used almost every option of xsl:sort. I thought of writing a blog post so others can easily find examples as well.
Basic Sorting
The xsl:sort instruction must be the first child of xsl:for-each. The select attribute defines the sort key.
Sort by name:
Descending Order
The order attribute controls the direction. The default is ascending.
Numeric Sorting
This is the most common sorting mistake. By default, xsl:sort compares values as text. So 100 comes before 20, because the text "1" comes before "2". Set data-type="number" to fix this.
<?xml version="1.0" encoding="UTF-8"?><xsl:stylesheet version="1.0"xmlns:xsl="http://www.w3.org/1999/XSL/Transform"><xsl:output method="text"/><xsl:template match="/"><xsl:for-each select="employees/employee"><xsl:sort select="salary"/><!-- Text sort. 100000 comes before 78000. --><xsl:value-of select="name"/><xsl:if test="position() != last()">, </xsl:if></xsl:for-each></xsl:template></xsl:stylesheet>
Correct:
<?xml version="1.0" encoding="UTF-8"?><xsl:stylesheet version="1.0"xmlns:xsl="http://www.w3.org/1999/XSL/Transform"><xsl:output method="text"/><xsl:template match="/"><xsl:for-each select="employees/employee"><xsl:sort select="salary" data-type="number" order="descending"/><xsl:value-of select="concat(name, ' ', salary)"/><!-- Returns Amara 92000, Nimal 85000, Kasun 78000 --><xsl:if test="position() != last()">, </xsl:if></xsl:for-each></xsl:template></xsl:stylesheet>
In XSLT 2.0 or later, we can also cast the key instead.
Sorting by Multiple Keys
We can add several xsl:sort instructions. The first one is the primary key. The second one breaks the ties.
<?xml version="1.0" encoding="UTF-8"?><xsl:stylesheet version="1.0"xmlns:xsl="http://www.w3.org/1999/XSL/Transform"><xsl:output method="text"/><xsl:template match="/"><xsl:for-each select="employees/employee"><xsl:sort select="department"/><!-- First sort by department --><xsl:sort select="salary" data-type="number" order="descending"/><!-- Then by salary inside each department --><xsl:value-of select="concat(department, ' ', name)"/><xsl:if test="position() != last()">, </xsl:if></xsl:for-each></xsl:template></xsl:stylesheet>
Case Insensitive Sorting
By default, sorting can behave differently for upper and lower case letters depending on the processor. The safe way in XSLT 1.0 is sorting on a lower cased key using translate().
<?xml version="1.0" encoding="UTF-8"?><xsl:stylesheet version="1.0"xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema"><xsl:output method="xml"/><xsl:template match="/"><xsl:for-each select="employees/employee"><xsl:sort select="translate(name, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')"/><xsl:value-of select="name"/><xsl:if test="position() != last()">, </xsl:if></xsl:for-each></xsl:template></xsl:stylesheet>
In XSLT 2.0 or later, we can simply use lower-case().
<xsl:sort select="lower-case(name)"/>
Language Aware Sorting
Sorting rules differ between languages. The lang attribute tells the processor which language rules to use.
<xsl:sort select="name" lang="de"/><!-- German sorting rules -->
You can use an online XSLT playground such as xslttest or XSLT Tester VS Code Plugin to verify the examples listed above. Please refrain from copying content to other websites
No comments:
Post a Comment