XSL-Übersicht / xsl:sort, xsl:perform-sort, fn:sort

xsl:sort, xsl:perform-sort, fn:sort

xsl:sort, xsl:perform-sort, fn:sort

➪ Eine häufige Aufgabe ist die Sortierung von Ergebnissen. Hier bieten XSLT und XPath mit xsl:sort, xsl:perform-sort und fn:sort leistungsfähige Unterstützung.

Schauen wir uns zunächst die Logik ohne Sortierung an.


<xsl:for-each select="//Gehalt">
    <einkommen>
        <xsl:value-of select="."/>
    </einkommen>
</xsl:for-each>

Das (noch unsortierte) Ergebnis sieht so aus:


<einkommen>234.56</einkommen>
<einkommen>1234.56</einkommen>
<einkommen>5430</einkommen>
<einkommen>321.45</einkommen>
<einkommen>987.58</einkommen>
<einkommen>654.21</einkommen>
<einkommen>333.33</einkommen>
<einkommen>456</einkommen>
<einkommen>876.54</einkommen>
<einkommen>546.77</einkommen>
<einkommen>777.77</einkommen>
<einkommen>357</einkommen>
<einkommen>6789</einkommen>
<einkommen>234</einkommen>
<einkommen>321</einkommen>
<einkommen>456</einkommen>
<einkommen>3450</einkommen>
<einkommen>222</einkommen>
<einkommen>135</einkommen>

Sortierungen mit xsl:sort

Nun erfolgt die Sortierung mit Hilfe von xsl:sort. Da die Default-Sortierung data-type="text" ist, muß der Prozessor angewiesen werden, die Einzelwerte als "number" zu erkennen. order="descending" definiert die absteigende Sortierung, Alternative: order="ascending".


<xsl:for-each select="//Gehalt">
    <xsl:sort select="." data-type="number" order="descending"/>
    <einkommen>
        <xsl:value-of select="."/>
    </einkommen>
</xsl:for-each>

Ich denke, das Ergebnis ist nachvollziehbar:


<einkommen>6789</einkommen>
<einkommen>5430</einkommen>
<einkommen>3450</einkommen>
<einkommen>1234.56</einkommen>
<einkommen>987.58</einkommen>
<einkommen>876.54</einkommen>
<einkommen>777.77</einkommen>
<einkommen>654.21</einkommen>
<einkommen>546.77</einkommen>
<einkommen>456</einkommen>
<einkommen>456</einkommen>
<einkommen>357</einkommen>
<einkommen>333.33</einkommen>
<einkommen>321.45</einkommen>
<einkommen>321</einkommen>
<einkommen>234.56</einkommen>
<einkommen>234</einkommen>
<einkommen>222</einkommen>
<einkommen>135</einkommen>

Sortierungen mit fn:sort

Nun gibt es auch die Möglichkeit, die Sortierung direkt in XPath aufzurufen:


<xsl:for-each select="sort(//Gehalt)">
    <einkommen>
        <xsl:value-of select="."/>
    </einkommen>
</xsl:for-each>

Per Default wird dabei jedes Item als Text angesehen, daher fällt das Sortierergebnis vorläufig so aus:


<einkommen>1234.56</einkommen>
<einkommen>135</einkommen>
<einkommen>222</einkommen>
<einkommen>234</einkommen>
<einkommen>234.56</einkommen>
<einkommen>321</einkommen>
<einkommen>321.45</einkommen>
<einkommen>333.33</einkommen>
<einkommen>3450</einkommen>
<einkommen>357</einkommen>
<einkommen>456</einkommen>
<einkommen>456</einkommen>
<einkommen>5430</einkommen>
<einkommen>546.77</einkommen>
<einkommen>654.21</einkommen>
<einkommen>6789</einkommen>
<einkommen>777.77</einkommen>
<einkommen>876.54</einkommen>
<einkommen>987.58</einkommen>

Durch ein einfaches Type-Casting auf xs:decimal wird jedes Item als Zahl anerkannt ...


<xsl:for-each select="sort(//xs:decimal(Gehalt))">
    <einkommen>
        <xsl:value-of select="."/>
    </einkommen>
</xsl:for-each>

... und entsprechend sortiert:


<einkommen>135</einkommen>
<einkommen>222</einkommen>
<einkommen>234</einkommen>
<einkommen>234.56</einkommen>
<einkommen>321</einkommen>
<einkommen>321.45</einkommen>
<einkommen>333.33</einkommen>
<einkommen>357</einkommen>
<einkommen>456</einkommen>
<einkommen>456</einkommen>
<einkommen>546.77</einkommen>
<einkommen>654.21</einkommen>
<einkommen>777.77</einkommen>
<einkommen>876.54</einkommen>
<einkommen>987.58</einkommen>
<einkommen>1234.56</einkommen>
<einkommen>3450</einkommen>
<einkommen>5430</einkommen>
<einkommen>6789</einkommen>

Die absteigende Sortierung ist Ihnen lieber? Bitte sehr, mit funktioniert auch das:


<xsl:for-each select="reverse(sort(//xs:decimal(Gehalt)))">
    <einkommen>
        <xsl:value-of select="."/>
    </einkommen>
</xsl:for-each>

Funktionsbasierte Sortierungen mit fn:sort

Ergänzend gibt es die Möglichkeit, durch Einsatz von anonymen Funktionen den Sortierungsschlüssel zu verändern.

Die im Folgenden implementierte function multipliziert den jeweiligen Dezimalwert mit 10, wenn er ursprünglich unter 300 lag.


<xsl:for-each 
     select="sort(//xs:decimal(Gehalt), '',  
     function($v){ if ($v &lt; 300 ) 
                   then ( $v * 10 ) 
                   else ( $v ) })">
    <einkommen>
        <xsl:value-of select="."/>
    </einkommen>
</xsl:for-each>

Jene function sorgt dafür, daß die folgenden Einzelwerte jeweils um den Faktor 10 multipliziert werden. Dadurch erhalten sie einen effektiven internen Sortierwert in Höhe von:


   135    -> 1350
   222    -> 2220
   234    -> 2340
   234.56 -> 2345.6

... und das führt letztlich zu einem modifizierten Ergebnis:


<einkommen>321</einkommen>
<einkommen>321.45</einkommen>
<einkommen>333.33</einkommen>
<einkommen>357</einkommen>
<einkommen>456</einkommen>
<einkommen>456</einkommen>
<einkommen>546.77</einkommen>
<einkommen>654.21</einkommen>
<einkommen>777.77</einkommen>
<einkommen>876.54</einkommen>
<einkommen>987.58</einkommen>
<einkommen>1234.56</einkommen>
<einkommen>135</einkommen>
<einkommen>222</einkommen>
<einkommen>234</einkommen>
<einkommen>234.56</einkommen>
<einkommen>3450</einkommen>
<einkommen>5430</einkommen>
<einkommen>6789</einkommen>

Textbasierte Sortierung mit Collations

Bei der textbasierten Sortierung können eine wichtige Rolle spielen. Freilich akzeptiert nicht jeder Prozessor die Collation "http://www.w3.org/2013/collation/UCA?lang=de;strength=primary". Hier bietet sich das "lang"-Attribut von "xsl:sort" an.


 <xsl:template name="sortByCollation">
  <xsl:variable name="vfeld">
   <i>Straße2</i>
   <i>Strasse1</i>
   <i>Weg</i>
   <i>Straße1</i>
   <i>Strasse2</i>
   <i>Pfad</i>
  </xsl:variable>
  <xsl:for-each select="$vfeld/i">
   <xsl:sort select="." lang="de"/>
   <!-- anstelle von
   <xsl:sort 
        select="."  
        collation="...s.o. "/>
   -->
   <y>
    <xsl:value-of select="."/>
   </y>
  </xsl:for-each>
 </xsl:template>

Und hiermit erfolgt auch diese Sortierung korrekt:


<root>
   <y>Pfad</y>
   <y>Strasse1</y>
   <y>Straße1</y>
   <y>Straße2</y>
   <y>Strasse2</y>
   <y>Weg</y>
</root>

Sortierungen mit xsl:perform-sort

Natürlich ist es auch möglich, mehrere Sortierungen durchzuführen; im Beispiel füllen wir eine Variable v1 mit einer sortierten Sequenz.


<xsl:variable name="v1">    
  <xsl:for-each 
       select="//Mensch">
    <xsl:sort 
         select="../name" 
         data-type="text" 
         order="descending"/>
    <xsl:sort 
         select="Gehalt" 
         data-type="number" 
         order="ascending"/>
    <xsl:sequence select="."/>
  </xsl:for-each>
</xsl:variable>

Alternativ können wir ab XSLT version 2.0 dieselbe Sortierung auch mit xsl:perform-sort durchführen, auch ohne Verwendung von xsl:for-each oder xsl:apply-templates.


<xsl:variable name="v2">
  <xsl:perform-sort 
       select="//Mensch">
    <xsl:sort 
         select="../name" 
         data-type="text" 
         order="descending"/>
    <xsl:sort 
         select="Gehalt" 
         data-type="number" 
         order="ascending"/>
  </xsl:perform-sort>
</xsl:variable>

wg / 9. Februar 2018



Fragen? Anmerkungen? Tips?

Bitte nehmen Sie Kontakt zu mir auf:

Vorname
Nachname
Mailadresse







Vielen Dank für Ihr Interesse an meiner Arbeit.


V.i.S.d.P.: Wilfried Grupe * Klus 6 * 37643 Negenborn

☎ 0151. 750 360 61 * eMail: info10@wilfried-grupe.de

www.wilfried-grupe.de/XSL_XPATH_sort.html