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 sinn­volle Pendants die entsprechenden Template-Regeln mit den für die Knoten pas­senden match-Attributen benötigt.

Es existiert alternativ auch die Möglichkeit, eine »explizite« Schleife zu definie­ren, bei deren Ausführung das Template, in dem sie steht nicht verlassen wer­den muss. Die zur Verarbeitung der Knoten (bzw. Items) benötigten Anwei­sungsblö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 <daten­satz>-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 unab­hä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 Überschrif­ten 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 zusam­mengestellt, 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 Schlei­fendurchlauf 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 aktuel­len 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 aus­gestaltet werden, dass die Formatierung derjenigen mit einzelnen Templates ent­spricht. 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 selbstver­ständlich auch anwendbar). In diesem Fall werden einfach drei Sor­tiervorschriften 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 funkti­oniert und ebenfalls die Sortierung nach Preis pro Autor.

Was hier formuliert wurde, ist eine zusammengesetzte Sortiervorschrift. Pri­mä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 unterge­ordnete 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