Schleifen in XSLT mit xsl:for-each
(Auszug aus "XSLT 2.0 & XPath 2.0" von Frank Bongers, Kapitel 2.)
Eine Schleife ermöglicht das kontinuierliche Abarbeiten einer Reihe von Größen. In diesem Sinne stellt bereits xsl:apply-templates so etwas wie eine »implizite« Schleife dar: Die durch die Instruktion zusammengestellte Sequenz wird der Reihe nach Knoten für Knoten verarbeitet. Hierfür werden als sinnvolle Pendants die entsprechenden Template-Regeln mit den für die Knoten passenden match-Attributen benötigt.
Es existiert alternativ auch die Möglichkeit, eine »explizite« Schleife zu definieren, bei deren Ausführung das Template, in dem sie steht nicht verlassen werden muss. Die zur Verarbeitung der Knoten (bzw. Items) benötigten Anweisungsblöcke sind in der Schleifeninstruktion selbst beinhaltet. Die entsprechende Instruktion ist xsl:for-each.
Genau wie xsl:apply-templates benutzt xsl:for-each ein select-Attribut, um selbst eine Sequenz zusammenzustellen. Die ausgewählten Knoten werden dann der Reihe nach mit dem internen Anweisungsblock verarbeitet:
<xsl:template match="daten">
<table>
<xsl:for-each select="datensatz">
<tr><td><xsl:value-of select="."/></td></tr>
</xsl:for-each>
</table>
</xsl:template>
Dieses Template erzeugt eine HTML-Tabelle als Literal Result Element und nutzt die Schleife, um ebenso viele Tabellenzeilen auszugeben, wie <datensatz>-Elemente in der gebildeten Sequenz enthalten sind. Jedes <datensatz>-Element wird der Reihe nach zum aktuellen Knoten und sein Wert mit xsl:value-of in die Ergebnissequenz kopiert.
In diesem Fall ist die Verwendung von xsl:for-each nicht zwingend: Es wäre kein Problem, dasselbe Ergebnis mit xsl:apply-templates und zwei unabhängigen Templates zu erzielen. Die Tatsache, dass xsl:for-each sich seine Sequenz selbst zusammenstellt und auch selbst verarbeitet, gibt jedoch die Möglichkeit, beispielsweise ein Element im Inneren der Schleife anders zu behandeln als eine im Stylesheet ansonsten vorhandene Template-Regel.
Eine explizite Schleife kann etwa für die Erzeugung eines Inhaltsverzeichnisses eines umfangreicheren Dokuments verwendet werden, wofür die Überschriften herhalten sollen, die ansonsten jedoch anders verarbeitet werden:
<xsl:template match="buchdokument">
<h1>Inhaltsverzeichnis:</h1>
<xsl:for-each select="//ueberschrift">
<div><xsl:value-of select="."/></div>
</xsl:for-each>
<!-- normale Verarbeitung der Inhalte -->
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="ueberschrift">
...
</xsl:template>
Hier wird eine Sequenz aller <ueberschrift>-Knoten des Dokuments zusammengestellt, unabhängig von ihrer Hierarchiestufe. Diese Sequenz aktiviert jedoch nicht das bestehende Template für Überschriften, das erst später im Zuge der Ausgabe des Dokumentkörpers verwendet wird, sondern wird vor Ort durch xsl:for-each selbst verarbeitet.
Das jeweils ausgegebene Element wird innerhalb der Schleife für den Schleifendurchlauf zum Current Node; die Schleifensequenz ist aktuelle Sequenz. Werden hier die Funktionen fn:position() und fn:last() eingesetzt, so beziehen sie sich auf die entsprechenden Größen der Schleifensequenz. Die aktuelle Template-Regel bleibt jedoch die des umgebenden Templates.
Hinweis:
Eine alternative Lösung für diese Problemstellung ist mit Template-modi möglich, die unter Templates und Template-Mode vorgestellt werden.
Das Buchhandel-Stylesheet vereinfacht
Möchten oder müssen Sie das Stylesheet so weit vereinfachen, dass die gesamte Ausgabe innerhalb einer einzigen Template-Regel erfolgt, so stellt xsl:for-each einen möglichen Ansatz dar.
Hierfür muss mit xsl:for-each eine Schleife über alle <buch>-Elemente deklariert werden:
...
<xsl:template match="/">
<html>
<head>
<title>Buchhandel</title>
</head>
<body>
<h2>Mein XML-Buchhandel</h2>
<xsl:for-each select="buchhandel/buch">
<p><xsl:value-of select="."/></p>
</xsl:for-each>
</body>
</html>
</xsl:template>
...
Jedes <buch> wird entsprechend der Reihenfolge des Quelltextes zum aktuellen Knoten und sein Stringwert in einem <p>-Container ausgegeben. Dies ist, zugegeben, etwas primitiv gelöst, was sich leider auch in der Ausgabe bemerkbar macht.
Selbstverständlich kann der Sequenzkonstruktor innerhalb der Schleife so ausgestaltet werden, dass die Formatierung derjenigen mit einzelnen Templates entspricht. Hier werden nur die Bücher der Kategorie 'reisen' ausgegeben:
...
<xsl:for-each select="buchhandel/buch[@kat='reisen']">
<p><b><xsl:value-of select="buchtitel"/></b> von <i><xsl:value-of select="buchautor" separator=", "/></i>;<br/><b>Verlag: </b><xsl:value-of select="buchverlag"/>, <xsl:value-of select="@ersch-jahr"/> – <i><xsl:value-of select="preis"/><xsl:text> </xsl:text><xsl:value-of select="preis/@waehrung"/></i></p>
</xsl:for-each>
...
Das Ergebnis sieht annähernd wie gewohnt aus:
Yet Another Hitchhiker's Guide to the Galaxy von Adams, Douglas;
Verlag: Spacetrotter Publishing, 2001 – 17.00 Dollar
Unterm Gipfelkreuz von Assmann, Peter;
Verlag: Berg Verlag, 2002 – 14.00 Euro
Ganz oben von Hansen, Carl;
Verlag:Berg Verlag, 1960 – 20.95 Euro
Im Rahmen dieses Ansatzes kann auch leicht eine Sortierung vorgenommen werden. Allerdings hat die hierfür ebenfalls wieder verwendete Instruktion xsl:sort ein wenig mehr zu bieten, als bisher gezeigt wurde.
Sortieren von Sequenzen in xsl:for-each
Eine Sortierung der Buchliste soll jetzt ohne Beschränkung auf eine Kategorie, jedoch nach mehreren Sortierkriterien vorgenommen werden: Zunächst erfolgt eine Sortierung nach Verlag, anschließend nach Autor und schließlich nach Preis in absteigender Richtung.
Die Anwendung von xsl:sort in xsl:for-each erfolgt analog zu derjenigen in xsl:apply-templates (die hier gewonnenen Erkenntnisse sind dort selbstverständlich auch anwendbar). In diesem Fall werden einfach drei Sortiervorschriften nacheinander angewandt:
<xsl:for-each select="buchhandel/buch">
<xsl:sort select="buchverlag"/>
<xsl:sort select="buchautor[1]"/>
<xsl:sort select="preis" data-type="number" order="descending"/>
<p><b><xsl:value-of select="buchtitel"/></b> von <i><xsl:value-of select="buchautor" separator=", "/></i>;<br/><b>Verlag: </b><xsl:value-of select="buchverlag"/>, <xsl:value-of select="@ersch-jahr"/> – <i><xsl:value-of select="preis"/><xsl:text> </xsl:text><xsl:value-of select="preis/@waehrung"/></i></p>
</xsl:for-each>
Beim Buchautor ist der inzwischen geläufige Trick angewandt, um hier xsl:sort, das für sein Sortierkriterium einen String und keine Knotensequenz erwartet, nicht aus dem Konzept zu bringen:
Unterm Gipfelkreuz von Assmann, Peter;
Verlag: Berg Verlag, 2002 – 14.00 Euro
Bergwandern in den Alpen von Assmann, Peter;
Verlag: Berg Verlag, 2002 – 14.00 Euro
Wenn der Berg ruft von Assmann, Peter;
Verlag: Berg Verlag, 1989 – 12.00 Euro
Noch weiter oben von Hansen, Carl;
Verlag: Berg Verlag, 1961 – 23.95 Euro
Zunächst werden alle Bücher des »Berg Verlags« ausgegeben, gefolgt von denen des EDV Verlags. Auch die Sortierung nach Autor innerhalb der Verlage funktioniert und ebenfalls die Sortierung nach Preis pro Autor.
Was hier formuliert wurde, ist eine zusammengesetzte Sortiervorschrift. Primäre Sortiervorschrift ist die zuerst stehende xsl:sort-Instruktion. Von dieser als »gleich« angesehene Items werden anschließend in sich entsprechend der folgenden sekundären Vorschrift geordnet und so fort. Hierbei wird nirgends die durch eine übergeordnete Vorschrift erzielte Sortierung durch eine untergeordnete zerstört, sodass eine sichere kaskadierende Sortierung ermöglicht wird.
<< zurück | vor >> |
Tipp der data2type-Redaktion: Zum Thema XSLT bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an: |
Copyright © Galileo Press, Bonn 2008
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken.
Ansonsten unterliegt dieses Kapitel aus dem Buch "XSLT 2.0 & XPath 2.0 ― Das umfassende Handbuch" denselben Bestimmungen wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.
Galileo Press, Rheinwerkallee 4, 53227 Bonn