im trying to sort my transformed output by a element which contains a hex value.
<xsl:sort select="Generation/Sirio/Code" data-type="number"/>
Values are plain old Hex: 00 01 02 ... 0A ... FF
but they are getting sorted like that: 0A FF 00 01 02
, which indicates that the sorting method fails as soon as there are character involved.
How can i work around this?
Thank you very much!
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vZeroes"
select="'00000000000000000000000000000000000000'"/>
<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=
"concat(substring($vZeroes,
1,
string-length($vZeroes)
-
string-length(@hexVal)
),
translate(@hexVal,'abcdef','ABCDEF')
)
"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
when applied on this XML document:
<t>
<x hexVal="0A"/>
<x hexVal="0327FE"/>
<x hexVal="ff5240"/>
<x hexVal="1BA402"/>
<x hexVal="C3A214"/>
</t>
produces the wanted, correct result:
<t>
<x hexVal="0A"/>
<x hexVal="0327FE"/>
<x hexVal="1BA402"/>
<x hexVal="C3A214"/>
<x hexVal="ff5240"/>
</t>
Solution details:
The only assumption this solution makes is that the maximum length of the hexadecimal sort keys is limited (say to 20). Then we are using a string of zeroes, longer than this maximum length. The $vZeroes
variable holds this string of zeroes and is used in padding every sort key to the left with zeroes, so that all sort keys have the same length.
Simply remove data-type="number"
.
Your example will sort just fine as text (which is the default). In general, simply sorting as text is probably the best approach. XSLT 1.0 does not handle arbitrary data transformations very well, and if you try to use it as a "general" string processor, you'll end up with large, complex, hard to maintain transformations.
If you do sort as text, you'll need to ensure all numbers are padded with 0's on the left to the same length. All letters must be in the same case (which is almost always true anyhow, but might break if you combine data from different sources.) Usually, these requirements are easy to meet outside of XSLT.
The xslt-function translate(my-xpath-expr,'abcdef','ABCDEF')
could be used to transform mixed-case hexadecimals into upper-case hexadecimals. This probably isn't necessary, usually.
If you don't know the length of the hexadecimal number you can prepend '0' as dimitre's solution shows, but you can often get away with a simpler trick:
<xsl:sort select="string-length(Generation/Sirio/Code)" data-type="number"/>
<xsl:sort select="translate(Generation/Sirio/Code,'abcdef','ABCDEF')"/>
This works so long as smaller numbers are never longer than larger numbers. It also works if the numbers may contain spaces or are prefixed by "0x
".
However, if possible, you're best off simply ensuring all numbers are formatted identically and sorting by text - KISS where possible.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With