Bedingte Programmverarbeitung
(Auszug aus "Java und XSLT" von Eric M. Burke)
Das folgende Template (siehe Beispiel Schleifen und Sortierung, Zeile 95 bis 103) gibt Namen von Bundespräsidenten aus. Die grundlegende Aufgabe dieses Templates ist es, den Vornamen, Nachnamen und eventuell ein Mittelinitial auszugeben. Ein umbruchgeschütztes Leerzeichen wird zwischen jedem Namensbestandteil ausgegeben, damit die Felder nicht ineinanderrutschten. Was wir nicht berücksichtigt haben, ist die Tatsache, daß manche Bundespräsidenten gar keinen zweiten Vornamen und somit kein Mittelinitial haben, weswegen unser Template den Vornamen gefolgt von zwei Leerzeichen und dann den Nachnamen ausgibt. Um dies zu korrigieren, muß zunächst die Existenz eines zweiten Vornamens geprüft werden, bevor man den Inhalt und die Leerzeichen ausgibt. Dazu wird konditionale Logik eingesetzt, eine Funktionalität, die in nahezu jeder existierenden Programmiersprache vorhanden ist.
Beispiel: Schleifen und Sortierung
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="/">
<html>
<body>
<h1>Sortierbeispiele</h1>
<xsl:apply-templates select="bundespraesidenten"/>
</body>
</html>
</xsl:template>
<!--********************************************************************
** Bundespräsidenten-Template
*****************************************************************-->
<xsl:template match="bundespraesidenten">
<!--*****************************************************************
** Sortierung mit xsl:for-each
**************************************************************-->
<h2>Alle Bundespräsidenten mit xsl:for-each nach Vorname sortiert</h2>
<xsl:for-each select="bundespraesident">
<xsl:sort select="name/vorname"/>
<xsl:apply-templates select="name"/>
</xsl:for-each>
<!--*****************************************************************
** Sortierung mit xsl:apply-templates
**************************************************************-->
<h2>Alle Bundespräsidenten mit xsl:apply-templates nach Vorname sortiert</h2>
<xsl:apply-templates select="bundespraesident/name">
<xsl:sort select="vorname"/>
</xsl:apply-templates>
<h2>Alle Bundespräsidenten mit xsl:apply-templates nach Amtszeit</h2>
<xsl:apply-templates select="bundespraesident/name">
<xsl:sort select="../amtsperiode/@von" data-type="number" order="descending"/>
</xsl:apply-templates>
<!--*****************************************************************
** Sortierung über mehrere Felder
**************************************************************-->
<h2>Beispiel zur Sortierung über mehrere Felder</h2>
<xsl:apply-templates select="bundespraesident/name">
<xsl:sort select="nachname"/>
<xsl:sort select="vorname" order="descending"/>
</xsl:apply-templates>
<!--*****************************************************************
** Verschachtelte xsl:for-each-Schleifen
** (unter der Annahme, daß ein Bundespräsident Vorname, Mittelinitial
** und Name hat und ggf. vom Bundesratspräsidenten vertreten wird ...)
**************************************************************-->
<h2>Alle Bundespräsidenten und Bundesratspräsidenten mit xsl:for-each</h2>
<ul>
<xsl:for-each select="bundespraesident">
<xsl:sort select="name/vorname" order="descending"/>
<li>
<xsl:apply-templates select="name"/>
</li>
<ul>
<xsl:for-each select="bundesratspraesident">
<xsl:sort select="name/vorname"/>
<li>
<xsl:apply-templates select="name"/>
</li>
</xsl:for-each>
</ul>
</xsl:for-each>
</ul>
<!--*****************************************************************
** wie gerade, aber mit xsl:apply-templates
**************************************************************-->
<h2>Bundespräsidenten und Bundesratspräsidenten mit xsl:apply-templates</h2>
<ul>
<xsl:apply-templates select="bundespraesident">
<xsl:sort select="name/vorname" order="descending"/>
</xsl:apply-templates>
</ul>
</xsl:template>
<!--*****************************************************************
** Das 'Bundespraesident'-Template. Es gibt den Namen des Bundes-
** präsidenten aus und den des Bundesratspräsidenten.
**************************************************************-->
<xsl:template match="bundespraesident">
<li>
<xsl:apply-templates select="name"/>
</li>
<ul>
<xsl:for-each select="bundesratspraesident">
<xsl:sort select="name/vorname"/>
<li>
<xsl:apply-templates select="name"/>
</li>
</xsl:for-each>
</ul>
</xsl:template>
<!--*****************************************************************
** Name-Template, gibt Vorname, Mittelinitial und Nachname aus
**************************************************************-->
<xsl:template match="name">
<xsl:text disable-output-escaping="yes">&nbsp;</xsl:text>
<xsl:value-of select="vorname"/>
<xsl:text disable-output-escaping="yes">&nbsp;</xsl:text>
<xsl:value-of select="mittelinitial"/>
<xsl:text disable-output-escaping="yes">&nbsp;</xsl:text>
<xsl:value-of select="nachname"/>
<br/>
</xsl:template>
</xsl:stylesheet>
XSLT bietet zwei Mechanismen, die konditionale Logik unterstützen: <xsl:if> und <xsl:choose>. Sie erlauben einem Stylesheet, unterschiedliche Ausgaben zu erzeugen, abhängig von den Ergebnissen eines Booleschen Ausdrucks, der, wie in XPath definiert, true oder false zurückliefern muß.
Das Verhalten des <xsl:if>-Elements ist vergleichbar mit dem folgenden Java-Code:
if (boolescher Ausdruck) {
// mach irgendwas
}
In XSLT lautet die Syntax wie folgt:
Das test-Attribut ist erforderlich und muß einen Booleschen Ausdruck enthalten. Wenn das Ergebnis true ist, wird der Inhalt dieses Elements instantiiert; andernfalls wird er übersprungen. Der Code im folgenden Beispiel zeigt verschiedene Einsatzmöglichkeiten von <xsl:if> und verwandten XPath-Ausdrücken. Auf die hervorgehobenen Codezeilen wird in den nächsten Abschnitten näher eingegangen.
Beispiel: <xsl:if>-Beispiele
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<!--******************************************************
** "/"-Template
***************************************************-->
<xsl:template match="/">
<html>
<body>
<h1>Beispiele zu bedingter Verarbeitung</h1>
<xsl:apply-templates select="bundespraesidenten"/>
</body>
</html>
</xsl:template>
<!--******************************************************
** "bundespraesidenten"-Template
***************************************************-->
<xsl:template match="bundespraesidenten">
<h3>Liste von <xsl:value-of select="count(bundespraesident)"/>Bundespräsidenten</h3>
<ul>
<xsl:for-each select="bundespraesident">
<li>
<!-- jede zweite Zeile fettgedruckt ausgeben -->
<xsl:if test="(position( ) mod 2) = 0">
<xsl:attribute name="style">
<xsl:text>font-weight: bold;</xsl:text>
</xsl:attribute>
</xsl:if>
<xsl:apply-templates select="name"/>
<!-- nach dem letzten Element wird noch etwas Text ausgegeben -->
<xsl:if test="position() = last( )">
<xsl:text> (gegenwärtiger Bundespräsident)</xsl:text>
</xsl:if>
</li>
</xsl:for-each>
</ul>
</xsl:template>
<!--******************************************************
** "name"-Template
***************************************************-->
<xsl:template match="name">
<xsl:value-of select="nachname"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="vorname"/>
<xsl:if test="mittelinitial">
<xsl:text disable-output-escaping="yes">&nbsp;</xsl:text>
<xsl:value-of select="mittelinitial"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Zuerst gibt das match="bundespraesidenten"-Template eine Überschrift aus, die die Anzahl aller Bundespräsidenten ausgibt:
Liste von <xsl:value-of select="count(bundespraesidenten)"/> Bundespräsidenten
Die count( )-Funktion ist eine XPath-Knotensatz-Funktion, sie liefert die Anzahl aller Elemente in einem Knotensatz zurück. In diesem Fall ist der Knotensatz die Liste von <bundespraesident>-Elementen, die unmittelbare Kindelemente von <bundespraesidenten> sind. Es wird also die Zahl der Bundespräsidenten in der XML-Datei ausgegeben. Der nächste Codeblock macht die Hauptarbeit in diesem Stylesheet und gibt jeden Bundespräsidenten als Listenelement in einer Schleife aus:
<xsl:for-each select="bundespraesident">
<li>
<!-- jede zweite Zeile fettgedruckt ausgeben -->
<xsl:if test="(position( ) mod 2) = 0">
<xsl:attribute name="style">
<xsl:text>font-weight: bold;</xsl:text>
</xsl:attribute>
</xsl:if>
...
In diesem Beispiel wählt die <xsl:for-each>-Schleife zunächst alle <bundespraesident>-Elemente, die unmittelbare Kindelemente des <bundespraesidenten>-Elements sind. Während die Schleife diesen Knotensatz durchläuft, liefert die position( )-Funktion einen Integer zurück, der die aktuelle Knotenposition in der aktuellen Knotenliste repräsentiert, angefangen mit dem Index 1. Der mod-Operator führt eine Modulo-Division durch und liefert den Rest zurück, so wie Java und ECMAScript es bei Verwendung des %-Operators machen. Der XPath-Ausdruck (position( ) mod 2) = 0 liefert für gerade Zahlen true zurück; daher wird dem <li>-Tag für jeden zweiten Bundespräsidenten das style-Attribut hinzugefügt, das die Ausgabe fett erscheinen läßt.
Das Template geht wie folgt weiter:
...
<xsl:apply-templates select="name"/>
<!-- nach dem letzten Element wird noch etwas Text ausgegeben -->
<xsl:if test="position() = last( )">
<xsl:text> (gegenwärtiger Bundespräsident)</xsl:text>
</xsl:if>
</li>
</xsl:for-each>
Die last( )-Funktion liefert einen Integer zurück, der die Größe des aktuellen Kontexts angibt; in diesem Fall liefert sie die Zahl der Bundespräsidenten. Wenn die Position dieser Anzahl entspricht, wird der Zusatztext (gegenwärtiger Bundespräsident) an den Ergebnisbaum angehängt. Java-Programmierer sollten beachten, daß XPath für Vergleiche ein einfaches =-Zeichen anstelle des bei Java üblichen == verwendet. Das HTML für unsere Liste wird am Ende etwa so aussehen:
<li>Heuss, Theodor</li>
<li style="font-weight: bold;">Lübke, Heinrich</li>
<li>Heinemann, Gustav W.</li>
<li style="font-weight: bold;">Scheel, Walter</li>
<li>Carstens, Karl</li>
<li style="font-weight: bold;">v. Weizsäcker, Richard</li>
<li>Herzog, Roman</li>
<li style="font-weight: bold;">Rau, Johannes (gegenwärtiger Bundespräsident)</li>
Die Ausgabe wurde gegenüber der aus dem Beispiel Schleifen und Sortierung verbessert und verwendet nun <xsl:if>, um zu ermitteln, ob ein zweiter Vorname bzw. ein Mittelinitial vorhanden ist:
<xsl:template match="name">
<xsl:value-of select="nachname"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="vorname"/>
<xsl:if test="mittelinitial">
<xsl:text disable-output-escaping="yes">&nbsp;</xsl:text>
<xsl:value-of select="mittelinitial"/>
</xsl:if>
</xsl:template>
In diesem Fall ermittelt <xsl:if test="mittelinitial"> die Existenz eines Knotensatzes anstatt den Wert eines Booleschen Ausdrucks. Wenn <mittelinitial>-Elemente gefunden wurden, wird der Inhalt von <xsl:if> instantiiert.
Wenn <mittelinitial>-Elemente gefunden werden, wird das erste ausgegeben. Später, im Beispiel NamensFormatierung.xslt, wird <xsl:for-each> eingesetzt, um mehrere optionale Elemente, in diesem Fall dann die Titel, wie bei Dr. Dr. Gustav W. Heinemann, auszugeben.
Die Überprüfung auf Existenz eines Attributs ist der Existenzüberprüfung für ein Element sehr ähnlich. Zum Beispiel:
Anders als bei den meisten Programmiersprachen kennt <xsl:if> kein korrespondierendes else oder otherwise. Das bereitet uns jedoch nur geringfügige Unannehmlichkeiten (<xsl:choose> erfordert viel Tipparbeit.), denn das <xsl:choose>-Element bietet diese Funktionalität.
<xsl:choose>, <xsl:when> und <xsl:otherwise>
Das XSLT-Äquivalent zu Javas switch-Statement ist <xsl:choose>, das zumindest in seiner Funktionsweise identisch (Javas switch-Statement funktioniert nur mit char, byte, short oder int) ist. <xsl:choose> erwartet ein oder mehrere <xsl:when>-Elemente, gefolgt von einem optionalen <xsl:otherwise>-Element. Das folgende Beispiel <xsl:choose> zeigt die Verwendung dieser Funktionalität. Das Beispiel verwendet zusätzlich noch das im nächsten Abschnitt besprochene <xsl:variable>-Element.
Beispiel: <xsl:choose>
<xsl:template match="bundespraesidenten">
<h3>Farblich gekennzeichnet nach Parteizugehörigkeit</h3>
<ul>
<xsl:for-each select="bundespraesident">
<xsl:variable name="farbe">
<!-- hier wird die Farbe den Parteien zugeordnet -->
<xsl:choose>
<xsl:when test="partei = 'SPD'">
<xsl:text>red</xsl:text>
</xsl:when>
<xsl:when test="partei = 'CDU'">
<xsl:text>black</xsl:text>
</xsl:when>
<xsl:when test="partei = 'FDP'">
<xsl:text>yellow</xsl:text>
</xsl:when>
<!-- hier nie gebraucht -->
<xsl:otherwise>
<xsl:text>gray</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<li>
<font color="{$farbe}">
<xsl:apply-templates select="name"/>
<xsl:text> - </xsl:text>
<!-- den Namen der Partei anzeigen -->
<xsl:value-of select="partei"/>
</font>
</li>
</xsl:for-each>
</ul>
</xsl:template>
In diesem Beispiel wird die Liste der Bundespräsidenten mit deren Parteizugehörigkeit ausgegeben. Das Element <xsl:when> ordnet für alle möglichen Parteien einer Variable einen Wert zu. Diese Variable, farbe, wird dann in einem font-Tag verwendet, um die Ausgabe für jede Partei in einer anderen Farbe erscheinen zu lassen. Das <xsl:otherwise>-Element wird niemals ausgeführt, weil kein Bundespräsident einer anderen Partei als den aufgelisteten angehört hat. Wenn ein neuer Bundespräsident einmal einer anderen Partei angehören sollte, dann würde keine der <xsl:when>-Bedingungen zutreffen und die Schriftfarbe auf grau gesetzt.
Ein Unterschied zwischen der XSLT-Methode und einem reinen Java-Ansatz ist, daß XSLT keine break-Statements zwischen den <xsl:when>-Elementen verlangt. In XSLT werden die <xsl:when>-Elemente in der Reihenfolge verarbeitet, in der sie auftauchen, wobei nur das erste Element verarbeitet wird, dessen Ausdruck true zurückliefert, alle anderen werden übersprungen. Wenn kein <xsl:when>-Element paßt, wird, falls vorhanden, das <xsl:otherwise>-Element verarbeitet.
Da <xsl:if> über kein korrespondierendes <xsl:else> verfügt, kann <xsl:choose> verwendet werden, um die gewünschte Funktionalität zu imitieren:
<xsl:choose>
<xsl:when test="Bedingung">
<!-- if-Bedingung -->
</xsl:when>
<xsl:otherwise>
<!-- else-Bedingung -->
</xsl:otherwise>
</xsl:choose>
Wie schon in anderen Teilen von XSLT, fordert die XML-Syntax zwar ein wenig mehr Tipparbeit, als Java-Programmierer gewohnt sind, dafür muß auf die Mechanismen von if/else aber nicht verzichtet werden.
<< zurück | vor >> |
Tipp der data2type-Redaktion: Zum Thema Java & XSLT bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an: |
Copyright © 2002 O'Reilly Verlag GmbH & Co. KG
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken.
Ansonsten unterliegt dieses Kapitel aus dem Buch "Java und XSLT" 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.
O’Reilly Verlag GmbH & Co. KG, Balthasarstraße 81, 50670 Köln, kommentar(at)oreilly.de