views:

48

answers:

3

Hi, 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!

+1  A: 

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.

Eamon Nerbonne
Only if all the strings are of the same length though
nos
Refresh, I already added a note about that ;-).
Eamon Nerbonne
The "simpler trick" will incorrectly sort "0003' and "A2".
Dimitre Novatchev
@dimitre This is true and it was already said that this works only for strings with the same length. Luckily thats the matter in my files, since they are created by my own tool and only used internally. While your solution is clearly better, this one just works for me. However, I marked yours correct.
atamanroman
@fielding: Thanks. It doesn't matter whether the accepted answer is mine or someone else's but that it is the best, most correct answer. Even more important than the utility of the answer to the OP is its "cognitive value" to the many people who are reading it and trying to copy it in their practices.
Dimitre Novatchev
@dimitre: I avoided the prefix-with-zero's approach for a reason. XSLT is ill-suited to this kind of data transformation and using that kind of fixup will make your code harder to read and longer. Also; it's fragile: Common hexadecimal notation such as "00 23 FF 01" or "0x123" won't work. Technically, you *can* work around that kind of limitation, but you really are better off *not* trying to hack that into XSLT but rather ensuring a consistent format and documenting that requirement.
Eamon Nerbonne
+2  A: 

This transformation:

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
    <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.

Dimitre Novatchev