Parameter und Variablen

(Auszug aus "Java und XSLT" von Eric M. Burke)

Wie in anderen Programmiersprachen auch, ist es in XSLT oftmals wünschenswert, eine Variable zu definieren, deren Wert an unterschiedlichen Stellen in einem Stylesheet verfügbar ist. Wenn beispielsweise der Titel eines Buches wiederholt ausgegeben werden soll, macht es Sinn, den Titel in einer Variablen zu speichern, anstatt immer wieder durch die XML-Daten zu gehen und diesen wiederholt heraussuchen zu lassen. Es kann ebenfalls von Vorteil sein, eine Variable einmal zu deklarieren und diese dann als Parameter an ein oder mehrere Templates zu übergeben. Diese Templates verwenden oft <xsl:if> oder <xsl:choose>, um in Abhängigkeit von einem übergebenen Parameter unterschiedliche Inhalte zu erzeugen.

<xsl:variable>

Variablen werden in XSLT über das Element <xsl:variable> entweder global oder lokal definiert. Eine globale Variable wird im »Top-Level« eines Stylesheets definiert, was nichts anderes heißt, als daß sie außerhalb aller Templates als unmittelbares Kindelement des <xsl:stylesheet>-Elements definiert wird. Top-Level-Variablen sind im ganzen Stylesheet zugreifbar, sogar in Templates, die vor der Variablendeklaration stehen.

Der zweite Ort, an dem eine Variable definiert werden kann, ist innerhalb eines Templates. Solche Variablen sind nur für die Elemente, die der <xsl:variable>-Deklaration innerhalb dieses Templates folgen, und deren Nachkommen zugreifbar. Der Code im Beispiel <xsl:choose> hat die Form von <xsl:variable> als Mechanismus zur Definition des Fonts gezeigt.

Variablen definieren

Variablen können auf drei verschiedene Arten definiert werden:

<xsl:variable name="homePage">index.html</xsl:variable>
<xsl:variable name="ersterTermin" select="termin [position() = first( )]/beginn"/>
<xsl:variable name="leer"/>

In der ersten Zeile spezifiziert der Inhalt von <xsl:variable> den Variablennamen. In diesem einfachen Beispiel wird der Text index.html der Variablen homePage zugewiesen. Natürlich kann der Inhalt komplexer ausfallen, wie auch schon im Beispiel <xsl:choose> gesehen. Die zweite Art, eine Variable zu definieren, basiert auf dem select-Attribut. Dessen Wert ist ein XPath-Ausdruck, mit dem wir in diesem Fall den letzten Termin in einer Liste selektieren. Schließlich kann eine Variable ohne ein select-Attribut oder Inhalt an einen leeren String gebunden werden. Die dritte Zeile ist äquivalent zu:

<xsl:variable name="leer" select="''"/>

Variablen verwenden

Um eine Variable einzusetzen, verweist man mit einem $-Zeichen auf den Variablennamen. Im folgenden Beispiel wird ein XPath-Lokalisierungspfad verwendet, um den Namen des aktuellen Bundespräsidenten zu ermitteln. Dieser Text wird dann in der Variable aktuellerBundespraesident gespeichert:

<xsl:variable name="aktuellerBundespraesident" select="bundespraesident[position() = last( )]/name"/>

Weiter unten in demselben Stylesheet kann die Variable aktuellerBundespraesident mit der folgenden Codezeile angezeigt werden:

<xsl:value-of select="$aktuellerBundespraesident"/>

Da das select-Attribut von <xsl:value-of> einen XPath-Ausdruck erwartet, wird $aktuellerBundespraesident als etwas Dynamisches angesehen und nicht als statischer Text. Um eine Variable innerhalb eines HTML-Attributwerts zu verwenden, müssen Sie die Attribute Value Template (AVT)-Syntax verwenden und die Variablenreferenz mit geschweiften Klammern einschließen:

<a href="{$homePage}">klicken Sie hier, um zur Homepage zu gelangen...</a>

Ohne die Klammern würde die Variable als Text fehlinterpretiert und nicht als dynamisch angesehen.

Die Beschränkung von Variablen besteht darin, daß Sie nicht verändert werden können. Sie können eine Variable also unmöglich als Zähler in einer <xsl:for-each>-Schleife einsetzen. Das kann für Programmierer, die an die Verwendung von wiederbeschreibbaren Variablen gewöhnt sind, sehr frustrierend sein. Allerdings kann es oftmals mit ein wenig Scharfsinn umgangen werden. Gewöhnlich wird man einem Template statt einer globalen Variable einen Parameter übergeben und dann das Template mit erhöhtem Parameterwert rekursiv aufrufen. Ein Beispiel für diese Technik wird gleich gezeigt.

Ein anderer XSLT-Trick beinhaltet die Kombination der Variablen-Initialisierung mit <xsl:choose>. Da Variablen nicht verändert werden können, ist es unmöglich, eine Variable zu deklarieren und erst später ihren Wert zu setzen. Der Workaround liegt in der Plazierung der Variablendefinition als Kindelement von <xsl:variable>, beispielsweise wie folgt in Verbindung mit <xsl:choose>:

<xsl:variable name="mittelName">
   <xsl:choose>
      <xsl:when test="mittelinitial">
         <xsl:value-of select="mittelinitial"/>
      </xsl:when>
      <xsl:otherwise>
         <xsl:text> </xsl:text>
      </xsl:otherwise>
   </xsl:choose>
</xsl:variable>

Dieser Code definiert eine Variable mit Namen mittelName. Wenn das Element <mittelinitial> vorhanden ist, wird dessen Wert der Variablen mittelName zugewiesen. Andernfalls wird ein Leerzeichen zugewiesen.

<xsl:call-template> und Named-Templates

Bis zu diesem Punkt sind alle Templates eng mit den eigentlichen Daten in der XML-Quelle verbunden gewesen. Das folgende Template paßt beispielsweise auf ein <angestellter>-Element, daher muß <angestellter> in Ihren XML-Daten enthalten sein:

<xsl:template match="angestellter">
...Inhalt, beispielsweise Name und Sozialversicherungsnummer des Angestellten
</xsl:template>

In vielen Fällen möchten Sie dieses Template aber vielleicht auch für andere Elemente als <angestellter> verwenden. Zusätzlich zu den <angestellter>-Elementen möchten Sie den gleichen Code auch einsetzen, um Informationen über einen <programmierer> oder <manager> auszugeben. Unter diesen Umständen kann <xsl:call-template> eingesetzt werden, um ein Template explizit mit dessen Namen aufzurufen, anstatt nach einem Muster in den XML-Daten zu gehen. Das Template wird die folgende Form haben:

<xsl:template name="formatiereSVNr">
...Inhalt
</xsl:template>

Dieses Template wird für die folgenden XML-Daten eingesetzt, wobei sowohl <manager> als auch <programmierer>-Elemente das Attribut svnr für die Sozialversicherungsnummer haben. Mit einem einzelnen sogenannten Named-Template, also einem Template mit einem name-Attribut, umgehen Sie die Notwendigkeit, mehrere Templates zu erstellen, nämlich jeweils eins für <manager> und ein anderes für <programmierer>. Wir werden ein Beispiel-Stylesheet ansehen, wenn wir Parameter diskutieren.

<?xml version="1.0" encoding="ISO-8859-1"?>
<team>
   <manager svnr="4230568737">
      <name>Daniel Bosen</name>
   </manager>
   <programmierer svnr="3593776766">
      <name>Frank Moritz</name>
   </programmierer>
   <programmierer svnr="9953885777">
      <name>Frank Müller</name>
   </programmierer>
</team>

<xsl:param> und <xsl:with-param>

Es ist schwierig, Named-Templates ohne Parameter zu nutzen, und Parameter können auch für normale Templates verwendet werden. Parameter gestatten, daß sich dasselbe Template, abhängig von den vom Aufrufer gelieferten Daten, unterschiedlich verhält. Daraus entstehen mehrfach verwendbare Codefragmente. Bei einem Named-Template erlauben Parameter die Übergabe von Daten an ein Template, wie es sich für die Sozialversicherungsnummer anbietet. Das folgende Beispiel enthält ein komplettes Stylesheet, das demonstriert, wie man den Parameter svnr an ein Named-Template übergibt.

Beispiel: NamedTemplate.xslt

<?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>
            <h3>Team-Mitglieder</h3>
            <ul>
               <xsl:for-each select="team/manager|team/programmierer">
                  <xsl:sort select="name"/>
                  <li>
                     <xsl:value-of select="name"/>
                     <xsl:text>, svnr = </xsl:text>
                     <xsl:call-template name="formatiereSVNR">
                        <xsl:with-param name="svnr" select="@svnr"/>
                     </xsl:call-template>
                  </li>
               </xsl:for-each>
            </ul>
         </body>
      </html>
   </xsl:template>
   <!-- ein Named-Template, das die 10-stellige SVNr durch Einfügen von '-'-Zeichen formatiert -->
   <xsl:template name="formatiereSVNR">
      <xsl:param name="svnr"/>
      <xsl:value-of select="substring($svnr, 1, 3)"/>
      <xsl:text>-</xsl:text>
      <xsl:value-of select="substring($svnr, 4, 2)"/>
      <xsl:text>-</xsl:text>
      <xsl:value-of select="substring($svnr, 6)"/>
   </xsl:template>
</xsl:stylesheet>

Dieses Stylesheet zeigt die Manager und Programmierer nach Namen sortiert in einer Liste an. Das <xsl:for-each>-Element selektiert die Vereinigungsmenge aus team/manager und team/programmierer, damit alle Manager und Programmierer aufgelistet werden. Der Pipe-Operator (|) berechnet die Vereinigungsmenge seiner zwei Operanden:

<xsl:for-each select="team/manager|team/programmierer">

Für jeden Manager oder Programmierer wird das <name>-Element gedruckt, gefolgt vom Wert des svnr-Attributs, das als Parameter an das formatiereSVNR-Template übergeben wird. Die Übergabe von einem oder mehreren Parametern wird durch Hinzufügen von <xsl:with-param> als Kindelement von <xsl:call-template> realisiert. Um zusätzliche Parameter zu übergeben, führen Sie einfach zusätzliche <xsl:with-param>-Elemente als Kindelemente von <xsl:call-template> auf. Am Ende wird <xsl:param> wie folgt verwendet:

<xsl:template name="formatiereSVNR">
   <xsl:param name="svnr"/>
   ...

In diesem Fall wird der svnr-Parameter standardmäßig einen leeren String enthalten, wenn kein Wert übergeben wurde. Um einen Standardwert für einen Parameter anzugeben, verwendet man das select-Attribut. Im folgenden Beispiel werden die Nullen in Hochkommas eingeschlossen, damit der Standardwert als String und nicht als XPath-Ausdruck angesehen wird:

<xsl:param name="svnr" select="'000000000'"/>

Innerhalb des formatiereSVNR-Templates können Sie sehen, daß die substring( )-Funktion Teile des Sozialversicherungsnummer-Strings selektiert. Näheres zur substring( )-Funktion und anderen String-Funktionen sehen wir später in diesem Kapitel.

Variablen inkrementieren

Leider gibt es in XSLT keine Standardmethode zur Inkrementierung von Variablen. Ist eine Variable einmal definiert, kann sie nicht mehr verändert werden. Das ist vergleichbar mit dem final-Feld in Java. Unter bestimmten Umständen kann mittels Rekursion in Verbindung mit Template-Parametern ein vergleichbares Ergebnis erzielt werden. An den XML-Daten im Beispiel stammbaum.xml wird ein solcher Lösungsansatz gezeigt.

Beispiel: stammbaum.xml

<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="stammbaum.xslt"?>
<person name="Otto">
 <person name="Sandra">
  <person name="Jeremy">
   <person name="Eliana"/>
  </person>
  <person name="Eric">
   <person name="Aidan"/>
  </person>
  <person name="Philip">
   <person name="Alex"/>
   <person name="Andy"/>
  </person>
 </person>
</person>

Wie Sie sehen, sind die XML-Daten rekursiv strukturiert. Jedes <person>-Element kann eine beliebige Zahl von <person>-Kindelementen beinhalten, die wiederum weitere <person>-Kindelemente haben können. Das ist natürlich nur ein stark vereinfachter Stammbaum, aber ein solches rekursives Muster taucht in vielen XML-Dokumenten auf. Wenn dieser Stammbaum angezeigt wird, sollte der Text analog zur Generation eingerückt werden. Otto wäre dann also die Wurzel, Sandra wäre um ein Leerzeichen eingerückt, und ihre Kinder wären um ein weiteres Leerzeichen eingerückt. Dies gibt einen visuellen Eindruck der Beziehung zwischen den Personen. Zum Beispiel:

Otto
 Sandra
  Jeremy
   Eliana
  Eric
   Aidan
  Philip
   Alex
   Andy

Das XSLT-Stylesheet, das diese Ausgabe erzeugt hat, ist im Beispiel stammbaum.xslt zu sehen.

Beispiel: stammbaum.xslt

<?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"/>
   <!-- hier beginnt die Verarbeitung -->
   <xsl:template match="/">
      <html>
         <body>
            <!-- wählt den ältesten Vorfahr -->
            <xsl:apply-templates select="person">
               <xsl:with-param name="ebene" select="'0'"/>
            </xsl:apply-templates>
         </body>
      </html>
   </xsl:template>
   <!-- Informationen über eine Person ausgeben und Kinder rekursiv selektieren. -->
   <xsl:template match="person">
      <xsl:param name="ebene"/>
      <!-- je nach Ebene einrücken -->
      <div style="text-indent:{$ebene}em">
         <xsl:value-of select="@name"/>
      </div>
      <!-- Rekursiv die Kinder auswählen und die Variable inkrementieren -->
      <xsl:apply-templates select="person">
         <xsl:with-param name="ebene" select="$ebene + 1"/>
      </xsl:apply-templates>
   </xsl:template>
</xsl:stylesheet>

Wie gewöhnlich beginnt dieses Stylesheet mit der Suche nach der Dokumentenwurzel und der Ausgabe eines HTML-Basisdokuments. Danach wählt es das <person>-Wurzelelement aus und übergibt ebene=0 als Parameter an das auf person passende Template:

<xsl:apply-templates select="person">
   <xsl:with-param name="ebene" select="'0'"/>
</xsl:apply-templates>

Das person-Template verwendet ein HTML-<div>-Tag, um den Namen jeder Person in einer neuen Zeile auszugeben, und spezifiziert die Texteinrückung in em. Die Cascading Style Sheets interpretieren ein em als dasselbe wie die Breite eines klein geschriebenen m im aktuellen Font. Schließlich wird das person-Template rekursiv mit dem Parameter $ebene + 1 aufgerufen. Obwohl so nicht der Wert der existierenden Variable erhöht wird, wird eine neue lokale Variable an das Template übergeben mit einem um eins höheren Wert. Anders als durch solche Tricks mit rekursiver Verarbeitung kann der Wert einer Variablen in XSLT nicht erhöht werden.

Template-Modi

Die letzte Variation von Templates ist die des Modus. Dieses Merkmal ist den Parametern ähnlich, aber ein wenig einfacher, woraus oft sauberer Code resultiert. Modi machen es möglich, daß mehrere Templates auf dasselbe Muster passen, wobei jedes einen anderen Arbeitsmodus verwendet. Ein Template kann Daten in einer Langfassung ausgeben, während ein anderes sie abkürzt. Es gibt keine vordefinierten Modi; Sie bestimmen sie alle. Das mode-Attribut sieht etwa wie folgt aus:

<xsl:template match="name" mode="langfassung">
   ...den vollen Namen ausgeben
</xsl:template>
<xsl:template match="name" mode="kurzfassung">
   ...den zweiten Vornamen auslassen
</xsl:template>

Um das korrekte Template zu instantiieren, muß dem <xsl:apply-templates>-Element ein mode-Attribut hinzugefügt werden:

<xsl:apply-templates select="bundespraesident/name" mode="langfassung"/>

Wenn das mode-Attribut weggelassen wird, sucht der Prozessor nach einem passenden Template, das keinen Modus unterstützt. Im hier gezeigten Code haben beide Templates einen Modus, daher muß auch im <xsl:apply-templates> ein Modus angegeben werden, sonst wird keins der zwei Templates instantiiert.

Ein vollständiges Stylesheet ist im Beispiel Template-Modi zu sehen. In diesem Beispiel taucht der Name eines Präsidenten entweder innerhalb einer Tabelle oder einer Liste auf. Anstatt einen Parameter an das bundespraesident-Template zu übergeben, werden zwei Arbeitsmodi definiert. Der tabelle(n)-Modus läßt das Template den Namen als Zeile einer Tabelle anzeigen. Im liste(n)-Modus wird der Name als HTML-Listenelement ausgegeben.

Beispiel: Template-Modi

<?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"/>
   <!-- ** zeigt, wie Template-Modi eingesetzt werden können -->
   <xsl:template match="/">
      <html>
         <body>
            <h2>Die Bundespräsidenten in einer HTML-Tabelle</h2>
            <table border="1">
               <tr>
                  <th>Nachname</th>
                  <th>Vorname</th>
               </tr>
               <xsl:apply-templates select="//bundespraesident" mode="tabelle"/>
            </table>
            <h2>Die Bundespräsidenten in einer unsortierten Liste</h2>
            <ul>
               <xsl:apply-templates select="//bundespraesident" mode="liste"/>
            </ul>
         </body>
      </html>
   </xsl:template>
   <!-- ** Den Namen eines Bundespräsidenten als Tabellenzeile anzeigen -->
   <xsl:template match="bundespraesident" mode="tabelle">
      <tr>
         <td>
            <xsl:value-of select="name/nachname"/>
         </td>
         <td>
            <xsl:value-of select="name/vorname"/>
         </td>
      </tr>
   </xsl:template>
   <!-- ** Den Namen eines Bundespräsidenten als Listenelement anzeigen -->
   <xsl:template match="bundespraesident" mode="liste">
      <li>
         <xsl:value-of select="name/nachname"/>
         <xsl:text>, </xsl:text>
         <xsl:value-of select="name/vorname"/>
      </li>
   </xsl:template>
</xsl:stylesheet>

Zusammenfassung der <xsl:template>-Syntax

Alle möglichen Variationen von <xsl:template> aufzuführen, scheint auf den ersten Blick eine schwierige Aufgabe zu sein, aber tatsächlich haben wir insgesamt nur drei Attribute kennengelernt:

match

Spezifiziert den Knoten in den XML-Daten, auf den das Template angewandt werden soll.

name

Legt unabhängig von den XML-Daten einen willkürlichen Namen für ein Template fest.

mode

Ähnlich dem Überladen einer Methode in Java erlaubt dies mehrere Versionen eines Templates, die auf dasselbe Muster passen.

Das einzige Attribut, das wir nicht detailliert besprochen haben, ist priority, das eingesetzt wird, um Konflikte bei sich überschneidenden Templates aufzulösen. Die XSLT-Spezifikation definiert eine sehr spezifische Schrittfolge, der Prozessoren folgen müssen, wenn mehr als eine Template-Regel zutrifft (siehe Abschnitt 5.5 der XSLT-Spezifikation). Mit Blick auf die Wartbarkeit von Code, ist es sicher eine gute Idee, überschneidende Template-Regeln in einem Stylesheet zu vermeiden. Bei der Kombination von mehreren Stylesheets wird man sich jedoch mit Konflikten bei den Template-Regeln konfrontiert sehen. In solchen Fällen kann das Festlegen eines höherwertigen numerischen Werts für die Priorität das Problem lösen. Die folgende Tabelle liefert ein paar zusammengefaßte Beispiele der verschiedenen Erscheinungsformen von <xsl:template>.

Tabelle: Zusammenfassung gebräuchlicher Template-Syntax

Template-Beispiel Bemerkungen
<xsl:template match="bundespraesident">
 ...
</xsl:template>
Paßt auf Bundespräsident-Knoten im XML-Quelldokument.
  
<xsl:template name="formatiereNamen">
 <xsl:param name="stil"/>
 ...
</xsl:template>
Definiert ein Named-Template, das in Verbindung mit <xsl:call-template> und <xsl:with-param> eingesetzt wird.
  
<xsl:template match="kunde" mode="meinModus">
 ...
</xsl:template>
Paßt auf kunde-Knoten, bei denen <xsl:apply-templates> zusätzlich mode="meinModus" verwendet.

   

<< 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