Als Tools für diesen Artikel wird Xalan in der Version 2.7 und als Editor Scite verwendet. Zuerst soll einmal aufgezeigt werden, welche Probleme und Limitationen es in XSLT 1.0 gibt. Zu jedem Problem soll dann, sofern vorhanden, eine entsprechende Lösung angeboten werden.
Der erste dieser Fälle ist das dynamische Referenzieren auf Variablen und Parameter per XPath-Ausdruck. Als Beispiel dient eine Liste von Variablen, deren Namen sich nur durch fortlaufende Nummern im Dokument unterscheiden:
<xsl:variable name="position_1">Erste Position</xsl:variable><xsl:variable name="position_2">Zweite Position</xsl:variable>
Die Annahme, dass man jetzt in einer for-each-Schleife bequem auf diese Variable durch Benutzung der position()-Funktion zugreifen könnte, ist falsch. Prinzipiell kann man keine Variablen-Referenzen zur Laufzeit erstellen. Im vorliegenden Beispiel wäre die naheliegende Lösung, ein externes XML-Dokument mit dem Inhalt der Variablen zu verwenden:
<variables><var name="position_1">Erste Position</var><var name="position_2">Zweite Position</var></variables>
In XSLT würde man dann über die document()-Funktion auf dieses XML-Dokument zurückgreifen und entsprechend in der for-each-Schleife darauf zugreifen (Listing 1).
Listing 1--------------------------------------------------------------------<!-- Einbinden des externen Dokuments --><xsl:variable name="varDoc" select="document('variables.xml')/variables"/>...<!-- Dynamischer Zugriff in einer for-each-Schleife --><xsl:for-each select="*"><!-- Hier generieren wir unseren dynamischen Variablennamen --><xsl:variable name="tmpVarName" select="concat('position_', position())"/><!-- Der Zugriff auf den Inhalt dieser Variablen erfolgt über unser XML --><xsl:variable name="dynVariable" select="$varDoc/var[@name=$tmpVarName]/text()"/><!-- Der Inhalt der dynamischen Variablen befindet sich jetzt in $dynVariable und kann von hier aus weiterverwendet werden --></xsl:for-each>
Zunächst holen wir uns das externe XML-Dokument in eine Variable. Dies geschieht durch die interne Funktion document(DATEINAME), die automatisch die angegebene Datei lädt und in ein eigenes DOM parst, das anschließend in unserer Variablen varDoc enthalten ist. Damit haben wir den vollen Zugriff mittels XSLT und XPath auf unser externes Dokument.
Innerhalb der for-each-Schleife soll nun je nach Position ein anderer Wert in unsere Variable einfließen. Da wir entsprechend die Werte in der Form position_x in unserem XML-Dokument angegeben haben, müssen wir in der Schleife nur noch den Namen zusammenbasteln und das Ganze dann aus unserem externen DOM auslesen. Das Zusammenbauen des Variablen-Namens geschieht durch die concat-Funktion, die einzelne Literals/Strings miteinander verbindet. Dort hängen wir dann noch die aktuelle Position, ermittelt durch die XPath-Funktion position() an. Wichtig ist zu erwähnen, dass die Funktion position() immer von eins zu zählen beginnt. Anschließend greifen wir auf unser externes XML zu, indem wir den Wert per select-Statement auslesen:
<xsl:variable name="dynVariable" select="$varDoc/var[@name=$tmpVarName]/text()"/>
Als root geben wir unser externes DOM an, dann selektieren wir das Unterelement var, das den aktuellen Variablennamen im Attribut name trägt; damit haben wir unseren Variablenwert dynamisch ausgelesen. Diese Methode hilft nur weiter, wenn der Inhalt der Variablen statisch ist und nicht erst zur Laufzeit ermittelt wird. In XSLT 1.0 haben wir leider keine Möglichkeit, dynamisch auf eine Variable zuzugreifen.
Variablen und Schleifen
Es ist bekannt, dass in XSLT 1.0 die Variablen eigentlich gar keine Variablen im traditionellen Sinne sind, sondern eigentlich nur eine Repräsentation von Konstanten, also Werten, die nur ein einziges Mal gesetzt werden können und danach nicht mehr veränderbar sind. Nähert man sich XSLT in dieser Form, wird man sehr schnell feststellen, wo die Probleme des Ganzen liegen, z.B. in Schleifen mit Abbruchsbedingungen. Angenommen, wir wollen eine Schleife erzeugen, die von Wert X bis auf Wert Y raufzählt und dabei für jeden Wert eine Textzeile ausgibt:Text in der 1. ZeileText in der 2.Zeile...Text in der 10. Zeile
In der klassischen Programmiersprache würde man einfach eine Schleife von 1 bis 10 durchlaufen lassen und für jeden Durchlauf eine Textzeile ausgeben lassen. In XSLT müssen wir auf einen Trick zurückgreifen, nämlich auf so genannte Named Templates. Da sich ein Template auch selbst wieder aufrufen kann, können wir damit ein rekursives Template bauen, das wie eine Schleife funktioniert (Listing 2).
Listing 2---------------------------------------------------------------------<xsl:template name="loop"><!-- Parameter für den Schleifendurchlauf --><xsl:param name="from">0</xsl:param><xsl:param name="inc">1</xsl:param><xsl:param name="to"/><!-- Code, der für jeden Durchlauf ausgeführt wird --><xsl:text>Text in der </xsl:text><xsl:value-of select="$from"/><xsl:text>. Zeile</xsl:text><!-- Rekursiver Aufruf des Templates --><xsl:if test="$from<=$to"><xsl:call-template name="loop"><xsl:with-param name="from" select="$from+$inc"/><xsl:with-param name="inc" select="$inc"/><xsl:with-param name="to" select="$to"/></xsl:call-template></xsl:if></xsl:template>
Zunächst setzen wir die Standard-Parameter from, inc und to. Diese werden benötigt, um den Begrenzungsrahmen für unsere Schleife festzulegen. Anschließend arbeitet dieses Template die Aktionen ab, die in der Schleife vonstatten gehen sollen. Danach wird getestet, ob die Obergrenze der Schleife schon erreicht wurde. Falls ja, wird die Rekursion abgebrochen und die Schleife damit beendet, falls nein, ruft das Template sich selbst wieder mit den gegebenen Parametern auf, erhöht allerdings den Wert des from-Parameter, damit der nächste Durchlauf von der nächsten Position erfolgt. Damit das Ganze auch dynamisch ist, kann man den Wert, um den erhöht werden soll, auch als Parameter inc angeben. Lässt man ihn weg, wird automatisch um eins erhöht. Der Aufruf der Schleife erfolgt durch das call-template-Element mit den zugehörigen Parametern. Soll die Schleife wie in unserem Beispiel von 1 bis 10 laufen, würde der Aufruf wie folgt aussehen:
<xsl:call-template name="loop"><xsl:with-param name="from">1</xsl:with-param><xsl:with-param name="to">10</xsl:with-param></xsl:call-template>




