Transformation

XSLT-1.0-Lösungen
Kommentare

Dank XSLT ist es leicht möglich, XML-Dokumente in textbasierte Formate zu transformieren, vorausgesetzt man hat einen kompatiblen XSLT-Prozessor und einige Tricks in der Hinterhand. Dieser Artikel soll näher auf einige Probleme, die mit XSLT auftreten können, eingehen und entsprechende Lösungswege anbieten.

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:

Erste Position
Zweite Position

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:

Erste Position
Zweite Position

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




...











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:

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. Zeile
Text 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).


0
1



Text in der 

. Zeile


<xsl:if test="$from







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:

1
10

Text richtig formatieren

Ein häufiger Anwendungsfall von XSLT ist die Generierung von formatierten HTML-Dateien aus einem oder mehreren XML-Dokumenten heraus. Oft wird dabei Text in CDATA-Abschnitten definiert, um die eigentliche Textformatierung (wie Zeilenumbrüche) aufrecht zu erhalten. Kopiert man jedoch den Ausgangstext einfach in das HTML-Dokument, so fallen die ganzen Zeilenumbrüche weg und man erhält eine einzige lange Zeile. Um dies zu umgehen, kann man sich ein einfaches Template basteln, das einen String in einen anderen String mit einem XML-Element ersetzt. Dieses kann man natürlich nicht nur für das Ersetzen eines Zeilenumbruches mit dem  -Tag verwenden, sondern dank Parametrisierung für jede Art von Stringersetzung. Ein rekursives Template, das einen String mit einem Tag ersetzt, zeigt Listing 3.



















Auch dieses Template arbeitet rekursiv. Zunächst wird getestet, ob das gesuchte Wort in unserem Ausgangstext enthalten ist. Ist dies nicht der Fall, wird die Rekursion abgebrochen. Ist das Wort enthalten, wird eine Ersetzung durch das Kopieren des gesamten Textes in Einzelteilen vorgenommen. Da damit immer nur ein Vorkommen des Wortes ersetzt werden kann, ruft sich das Template mit dem neuen Ausgangstext wieder auf und das Spiel beginnt von vorne, so lange, bis das gesuchte Wort nicht mehr gefunden werden kann. Um von einem Text alle Zeilenumbrüche mit dem  -Tag zu ersetzen, wird folgender Aufruf benutzt:



 

Der Parameter string enthält dabei den original Text, der ersetzt werden soll. Der Parameter from setzt fest, welche(s) Zeichen ersetzt werden soll, und der to-Parameter enthält das oder die Tags, die anstelle des gesuchten Strings eingesetzt werden sollen.

[ header = Seite 2: Zahlen und Formate ]

Zahlen und Formate

Als XSLT-Entwickler steht man oft vor der Aufgabe, Zahlen, Daten oder Strings in einer bestimmten Art und Weise zu formatieren. Als erstes Beispiel dient das Umwandeln von Telefonnummern. Oft erhält man Formate, die auch andere Zeichen als Zahlen enthalten, wie etwa (089) 4645-12. Damit man daraus eine vollständige, zahlenbasierte Telefonnummer generieren kann, genügt folgende Zeile:

(089)4645-12

Hier werden mittels der translate-Funktion alle Zeichen, die keine Zahlen sind, entfernt. Die Funktion erwartet folgende Parameter:

  • Ausgangstext
  • Zeichen, die übersetzt werden sollen
  • Zeichen, mit denen die des zweiten Parameters übersetzt werden sollen

Die translate-Funktion arbeitet nach folgendem Schema. Ist ein Zeichen im zweiten Parameter angegeben, aber wird im dritten Parameter keine Entsprechung dafür gefunden, dann wird dieses Zeichen im Ergebnis einfach weggelassen. In unserem Beispiel würde die Vorgehensweise also wie folgt aussehen:

  • Eingangstext ist (089)4645-12
  • Die erste translate-Funktion filtert alle Zahlen. Da im dritten Parameter keine Entsprechungen für die Zahlen 0–9 angegeben sind, werden diese im Ergebnis einfach weggelassen. Wir erhalten eine Liste mit allen restlichen Zeichen, hier: ()-
  • Nun liegen alle ungültigen Zeichen vor und wir machen uns nochmals die translate-Funktion zunutze. Diesmal übergeben wir allerdings nicht die Zahlen, sondern die soeben herausgefilterten Zeichen. Wir erhalten genau den gegenteiligen Effekt – nur die Ziffern bleiben erhalten: 089464512.

Ein weiteres praktisches Anwendungsbeispiel ist das Formatieren von Zahlen mit führenden Nullen. Als Beispiel dient die Zahl 123, aus der man einen String mit der Länge von sechs Zeichen erzeugen will, wobei führende Nullen verwendet werden. Dafür könnte man die format-number()-Funktion nutzen, aber zum Lernen von XSLT soll hier eine eigene, rekursive Routine dafür gebastelt werden (Listing 4).



0


=$digits">











Es ist erkennbar, dass rekursive Templates äußerst häufig benötigt werden. Diese ähneln im Ablauf dem Schleifen-Beispiel. Die Routine setzt so lange eine Null (bzw. das Zeichen, das im Parameter leading angegeben wird) vor den Ausgangstext, bis die gewünschte Zeichenlänge erreicht wurde. Ist dies der Fall, wird die neu formatierte Zahl einfach ausgegeben und die Routine damit beendet. Der Aufruf für unser Beispiel würde dann so erfolgen:

123


Listen gekonnt aufteilen

Oft kommt es vor, dass man ein XML-Dokument mit mehreren Einträgen hat, die man dann aufteilen möchte. Um dies zu erreichen, muss man zwei geschachtelte for-each-Schleifen verwenden. Ein XML-Dokument mit mehreren hundert Einträgen und der folgenden Struktur ist Beispiel:


John Smith


Michael Walker

...

Nun soll eine Tabelle mit je 20 Einträgen erstellt werden, in der dann in jeder Zeile der jeweilige Kundenname erscheint. Der entsprechende XSLT-Quellcode dazu sieht wie folgt aus:




<xsl:for-each select=".|following-sibling::item[position() 

In diesem Template wird zuerst eine Variable mit der maximalen Anzahl von Einträgen in einer Tabelle definiert. Dann werden mittels einer ersten for-each-Schleife alle Knoten ermittelt, die sich durch die Anzahl der Einträge mit Rest 1 teilen lassen. Dadurch erhalten wir eine Art Gruppierung, da nur die Knoten auf Position 1, 21, 41 usw. selektiert werden. Anschließend wird eine neue Tabelle erstellt und mittels einer zweiten for-each-Schleife durch alle Knoten durchgegangen, die dem aktuellen Knoten folgen, bis die maximale Anzahl der Knoten (20 in unserem Beispiel) erreicht wurde, dann beginnt das Spiel wieder von vorne.

Fazit

XSLT 1.0 bietet umfangreiche Möglichkeiten, XML-Dokumente in andere Formate zu transformieren. Allerdings gibt es vor allem in den Bereichen dynamische Variablen, dynamische Referenzen und der String-/Zeichen- und Datumsverarbeitung erhebliche Mängel. Diese sollen zwar in XSLT 2.0 bereinigt sein. Bis die gängigen XSLT-Prozessoren auch XSLT 2.0 komplett unterstützen, muss man sich wohl oder übel noch mit einigen Tricks bei XSLT 1.0 behelfen, um zu plausiblen Ergebnissen zu kommen. Vor allem muss man sehr oft auf rekursive Funktionen zurückgreifen, um zum Ziel zu gelangen, aber einige Features, wie zum Beispiel dynamische Variablen, funktionieren überhaupt nicht.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -