Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to sort by hexadecimals in xslt?

Tags:

sorting

hex

xslt

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!

like image 775
atamanroman Avatar asked Jun 14 '10 12:06

atamanroman


2 Answers

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.

like image 137
Dimitre Novatchev Avatar answered Oct 06 '22 23:10

Dimitre Novatchev


The simplest solution

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.

A slightly more general and longer solution

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.

like image 28
Eamon Nerbonne Avatar answered Oct 06 '22 23:10

Eamon Nerbonne