4

I need help with a small example, so that I can understand xsl:sort better.

My XML data looks like:

<NewTerms>
    <newTerm ID="3">Zebra</newTerm>
    <newTerm ID="11">Horse</newTerm>
    <newTerm ID="1">Cat</newTerm>
    <newTerm ID="90">Lion</newTerm>
    <newTerm ID="62">Jaguar</newTerm>
    <newTerm ID="30">Cheetah</newTerm>
    <newTerm ID="55">Deer</newTerm>
    <newTerm ID="45">Buffalo</newTerm>
    <newTerm ID="15">Dog</newTerm>
</NewTerms ID="10">

and I want to sort them according to the ID attribute. The XSL that I have is not working:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

    <xsl:output method="xml" indent="yes" omit-xml-declaration="no"/>

    <xsl:template match="@*|node()[not(preceding::node()=.)]">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()[not(preceding::node()=.)]">
                <xsl:sort select="./@ID"/>
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

I don't know how the xsl:sort function works. Help me through this example to get a better understanding of it.

Kenny Linsky
  • 1,726
  • 3
  • 17
  • 41
Jasmin
  • 75
  • 1
  • 4

3 Answers3

4

Your transform is correct. You have missed only to specify the data-type attribute which by default is "text" thus not working with numbers.

For more information about using xsl:sort you can see the specs and a recent similar question.

Here a few notes:

  • You can omit the ./@ID because . selects the context node which is the default in the template
  • You don't need to check for preceding nodes in that way, it does not make sense.

Notice You need only the Identity Transformation with a sort instruction.

Your final transform will look like:

<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    version="1.0">

    <xsl:output method="xml" indent="yes" omit-xml-declaration="no"/>
    <xsl:strip-space elements="*"/>

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()">
                <xsl:sort select="@ID" data-type="number"/>
      </xsl:apply-templates>
     </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

When applied to the input (here a bit modified to fix your end-tag typo):

<NewTerms>   
    <newTerm ID="3">Zebra</newTerm>
    <newTerm ID="11">Horse</newTerm> 
    <newTerm ID="1">Cat</newTerm>   
    <newTerm ID="90">Lion</newTerm>   
    <newTerm ID="62">Jaguar</newTerm>   
    <newTerm ID="30">Cheetah</newTerm>   
    <newTerm ID="55">Deer</newTerm>   
    <newTerm ID="45">Buffalo</newTerm>   
    <newTerm ID="15">Dog</newTerm> 
</NewTerms>

produces:

<NewTerms>
   <newTerm ID="1">Cat</newTerm>
   <newTerm ID="3">Zebra</newTerm>
   <newTerm ID="11">Horse</newTerm>
   <newTerm ID="15">Dog</newTerm>
   <newTerm ID="30">Cheetah</newTerm>
   <newTerm ID="45">Buffalo</newTerm>
   <newTerm ID="55">Deer</newTerm>
   <newTerm ID="62">Jaguar</newTerm>
   <newTerm ID="90">Lion</newTerm>
</NewTerms>
Community
  • 1
  • 1
Emiliano Poggi
  • 24,390
  • 8
  • 55
  • 67
1

The ID attribute in your clasing tag of NewTerms seems to be out of place.

The following XSL script sorts your data on the ID attribute:

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="xml"/>

  <xsl:template match="@*|node()">
    <xsl:copy-of select="." />
  </xsl:template>

  <xsl:template match="/NewTerms">
    <xsl:copy>
      <xsl:apply-templates select="newTerm">
        <xsl:sort select="@ID" data-type="number" />
      </xsl:apply-templates>
     </xsl:copy>
  </xsl:template>

</xsl:stylesheet>
rsp
  • 23,135
  • 6
  • 55
  • 69
  • 3
    Those ID attribute values look like numbers so doing '` is probably necessary to sort as requested. The default data type is "text" and that way for instance "110" would end up before "12". – Martin Honnen Jun 28 '11 at 16:02
0

As was pointed out in the comments to @rsp's answer, the default sort data-type is "text", but you want "number". You could actually use your own first attempt after making just that one change:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

  <xsl:output method="xml" indent="yes" omit-xml-declaration="no"/>

  <xsl:template match="@*|node()[not(preceding::node()=.)]">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()[not(preceding::node()=.)]">
        <xsl:sort select="./@ID" data-type="number" />
      </xsl:apply-templates>
     </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

This allows you to keep your (inefficient) duplicate check:

node()[not(preceding::node()=.)] 
Wayne
  • 59,728
  • 15
  • 131
  • 126