XML | XML-Schema | XPath | XSL-T | XSL-FO | XQuery | XProc | SVG |
XSL-T / Die XSLT - Struktur / xsl:sort, xsl:perform-sort, fn:sort / Sortieren und Filtern
![]() |
![]() |
➪ Häufig ist die Sortierung einer Sequenz auch mit einer Filterung verbunden. In diesem Abschnitt lesen Sie, wie Sie eine Elementliste sortieren und eine ausgewählte Menge selektieren können.
Auf dieser Seite:Nehmen Sie das folgende XML-Dokument:
<root>
<Ort name="Neustadt">
<Einwohner name="Brotlos" AnzahlBriefmarken="333"/>
<Einwohner name="Problemlos" AnzahlBriefmarken="2345"/>
<Einwohner name="Haltlos" AnzahlBriefmarken="123"/>
<Einwohner name="Achtlos" AnzahlBriefmarken="332"/>
<Einwohner name="Wolkenlos" AnzahlBriefmarken="555"/>
<Einwohner name="Traumschlos" AnzahlBriefmarken="732"/>
</Ort>
<Ort name="Kapstadt">
<Einwohner name="Holzflos" AnzahlBriefmarken="345"/>
<Einwohner name="Sprachlos" AnzahlBriefmarken="543"/>
<Einwohner name="Nixlos" AnzahlBriefmarken="12"/>
<Einwohner name="Lustlos" AnzahlBriefmarken="33"/>
<Einwohner name="Schlaflos" AnzahlBriefmarken="12"/>
<Einwohner name="Sorglos" AnzahlBriefmarken="987"/>
</Ort>
</root>
Die Aufgabe besteht darin, für jeden Ort jene drei Einwohner zu ermitteln, die die jeweils größte Anzahl an Briefmarken haben, und diese in sortierter Reihenfolge absteigend darzustellen. Gesucht ist also dieses Resultat:
<Ergebnis>
<Briefmarkenstadt name="Neustadt">
<Einwohner name="Problemlos" AnzahlBriefmarken="2345"/>
<Einwohner name="Traumschlos" AnzahlBriefmarken="732"/>
<Einwohner name="Wolkenlos" AnzahlBriefmarken="555"/>
</Briefmarkenstadt>
<Briefmarkenstadt name="Kapstadt">
<Einwohner name="Sorglos" AnzahlBriefmarken="987"/>
<Einwohner name="Sprachlos" AnzahlBriefmarken="543"/>
<Einwohner name="Holzflos" AnzahlBriefmarken="345"/>
</Briefmarkenstadt>
</Ergebnis>
Die Lösung in XSLT 1.0 arbeitet für jeden Ort mit einer simplen Sortierung der Einwohner nach der Anzahl ihrer Briefmarken. Innerhalb dieser Sortierung werden die ersten drei Elemente ausgewählt.
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<Ergebnis>
<xsl:for-each select="/root/Ort">
<Briefmarkenstadt name="{@name}">
<xsl:for-each select="Einwohner">
<xsl:sort
select="@AnzahlBriefmarken"
order="descending"
data-type="number"/>
<xsl:if test="position() < 4">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
</Briefmarkenstadt>
</xsl:for-each>
</Ergebnis>
</xsl:template>
</xsl:stylesheet>
Der Lösungsansatz mit xsl:apply-templates arbeitet ähnlich wie die XSLT 1.0-Lösung mit xsl:for-each: Innerhalb jedes Ort-Elements werden die Einwohner nach @AnzahlBriefmarken sortiert. Innerhalb dieser Sortierung werden in <xsl:template match="Einwohner"> die ersten drei Elemente ausgewählt.
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<Ergebnis>
<xsl:apply-templates select="/root/Ort"/>
</Ergebnis>
</xsl:template>
<xsl:template match="Ort">
<Briefmarkenstadt name="{@name}">
<xsl:apply-templates select="Einwohner">
<xsl:sort
select="@AnzahlBriefmarken"
order="descending"
data-type="number"/>
</xsl:apply-templates>
</Briefmarkenstadt>
</xsl:template>
<xsl:template match="Einwohner">
<xsl:if test="position() < 4">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Die Lösung in XSLT 1.1 arbeitet für jeden Ort mit einer temporären Variable v, in der die Einwohner bereits nach der Anzahl der Briefmarken sortiert sind. Anschließend werden die ersten drei Elemente dieser Variablen ausgewählt.
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="1.1">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<Ergebnis>
<xsl:for-each select="/root/Ort">
<Briefmarkenstadt name="{@name}">
<xsl:variable name="v">
<xsl:for-each select="Einwohner">
<xsl:sort
select="@AnzahlBriefmarken"
order="descending"
data-type="number"/>
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:variable>
<xsl:for-each
select="$v/Einwohner[position() < 4]">
<xsl:copy-of select="."/>
</xsl:for-each>
</Briefmarkenstadt>
</xsl:for-each>
</Ergebnis>
</xsl:template>
</xsl:stylesheet>
In XSLT 2.0 können Sie die Variable sortiert_nach_Briefmarken mithilfe von xsl:perform-sort erstellen und anschliessend über die subsequence-Funktion auswerten.
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<Ergebnis>
<xsl:for-each select="/root/Ort">
<Briefmarkenstadt name="{@name}">
<xsl:variable
name="sortiert_nach_Briefmarken"
as="element(Einwohner)*">
<xsl:perform-sort select="Einwohner">
<xsl:sort
select="@AnzahlBriefmarken"
data-type="number"
order="descending"/>
</xsl:perform-sort>
</xsl:variable>
<xsl:for-each
select="subsequence($sortiert_nach_Briefmarken, 1,3)">
<xsl:copy-of select="."/>
</xsl:for-each>
</Briefmarkenstadt>
</xsl:for-each>
</Ergebnis>
</xsl:template>
</xsl:stylesheet>
Die alternative Lösung in XSLT 3.0 arbeitet mit der XPath-Funktion for-each, die ihrerseits die reverse-Funktion aufruft: Das ist notwendig, weil die darin aufgerufene sort-Funktion per default aufsteigend sortiert. Von dem so erzeugten Ergebnis werden auch hier die ersten drei Elemente ausgewählt.
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="3.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<Ergebnis>
<xsl:for-each select="/root/Ort">
<Briefmarkenstadt name="{@name}">
<xsl:for-each select="
for-each
(
reverse
(
sort
(
Einwohner, (),
function ($p)
{$p/xs:integer(@AnzahlBriefmarken)}
)
), function ($v){$v}
)[position() < 4]">
<xsl:copy-of select="."/>
</xsl:for-each>
</Briefmarkenstadt>
</xsl:for-each>
</Ergebnis>
</xsl:template>
</xsl:stylesheet>
Natürlich können Sie auch hier anstelle [position() < 4] die subsequence-Funktion verwenden:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs" version="3.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<Ergebnis>
<xsl:for-each select="/root/Ort">
<Briefmarkenstadt name="{@name}">
<xsl:for-each
select="subsequence(
for-each
(
reverse
(
sort
(
Einwohner, (),
function ($p)
{$p/xs:integer(@AnzahlBriefmarken)}
)
), function ($v) {$v}
), 1,3)">
<xsl:copy-of select="."/>
</xsl:for-each>
</Briefmarkenstadt>
</xsl:for-each>
</Ergebnis>
</xsl:template>
</xsl:stylesheet>
In XQuery liest sich das so:
<Ergebnis>
{
for $o in /root/Ort return
<Briefmarkenstadt name="{$o/@name}">
{
for $e at $pos in for-each
(
reverse
(
sort
(
$o/Einwohner, (),
function ($p)
{$p/xs:integer(@AnzahlBriefmarken)}
)
), function ($v){$v}
)
return if ($pos < 4) then ($e) else nothing
}
</Briefmarkenstadt>
}
</Ergebnis>
wg / 22. April 2021
Fragen? Anmerkungen? Tipps?
Bitte nehmen Sie Kontakt zu mir auf.
V.i.S.d.P.: Wilfried Grupe * Klus 6 * 37643 Negenborn
☎ 0151. 750 360 61 * eMail: info10@wilfried-grupe.de