| 
Auslesen eines XML-Dokumentes
mit XSLT
Aufbau eines XSLT-Stylesheet
Die Funktion template match
Zugriff auf einen bestimmten Knoten aus einer
Gruppe gleichnamiger Knoten
Transformation in ein HTML-Dokument
Das Ergebnis der Transformation, den Ergebnisbaum,
direkt im Browser zeigen
Ermitteln des Wertes eines Attributes
Mehrere Attribute eines Knotens anzeigen lassen
Aufsummieren der Werte eines Attributes
Auslesen aller gleichnamigen Knoten in unterschiedlichen
Gruppen
Navigieren mit Hilfe von Achsen
Die Achse following-sibling
Die Achse preceding
Ein XML-Dokument mit XSLT modifizieren
Ein Element hinzufügen
Ändern eines Elementes
Löschen eines Elementes
Ein Element innerhalb des Dokumentes umpflanzen
Elemente sortieren mit XSLT
Datensätze nummerieren
Bedingte Anweisungen xsl:if und xsl:choose
Eine Bedingung von mehreren ist richtig: xsl:choose
Funktionen zum Arbeiten mit Zeichenketten
Die Funktion string-length
Die Funktion concat
Die Funktion contains
Die Funktion start-with
Die Funktion substring
Die Funktionen substring-before und substring-after
Anzahl der Knoten ermitteln
Den letzten Knoten ermitteln
X-beliebigen Knoten ermitteln
Auf gleichnamige Elemente zugreifen, die in
unterschiedlichen Knoten sind
| Auslesen
eines XML-Dokumentes mit XSLT |
|
XSLT (Extensible Stylesheet Language Transformation)
ermöglicht es, ein XML-Dokument zu transformieren, z.B.
in ein HTML-Dokument. Allerdings ist dies nicht der einzige
Ansatz. In dem Moment, in dem es gelingt, den Inhalt eines
Knotens, Text oder Attribute auszulesen, ist es möglich,
diesen Inhalt in HTML einzuwickeln. Man kann also auch mit
der DOM Spezifikation oder mit XPATH, siehe Auslesen einer
XML-Datei mit dem Perl Modul XML::LIBXML
und DomXML und PHP eine XML-Datei
transformieren. Aus didaktischen Gründen wünschen
wir uns eine Möglichkeit, diese Transformation von der
Kommandoebene (Dos Box, Eingabeaufforderung) durchzuführen.
Deshalb benötigen wir eine Software, die dies ermöglicht.
Am einfachsten geht das mit SAXON, den wir uns hier
downloaden können. Saxon ist ein C-Programm, dass beim
Aufruf zwei Paramater übergeben bekommt, die XML-Datei
und das Stylesheet und das dann die Transformation durchführt.
Optional ist ein dritter Parameter, wenn wir die transformierte
Datei auf der Festplatte speichern wollen. Alle folgenden
Beispiele gehen von der unten stehenden XML-Datei aus. Auf
XPATH, sozusagen das sql von XSLT, wird nur insoweit eingegangen,
als es nötig ist, um das Zusammenspiel mit XSLT zu verstehen.
Für eine detaillierte Darstellung von XPATH siehe Abfragen
mit XPATH.
<?xml version="1.0" encoding="ISO-8859-1"?>
<Projekte>
<Gruppe>
<Gruppenname stand="abgeschifft">Berliner
Verwaltungsreform</Gruppenname>
<Kollegen>
<Mitarbeiter Firma="Preis Wasserhaus">Werner
Lepinski</Mitarbeiter>
<Mitarbeiter>Erika Saufwech</Mitarbeiter>
<Mitarbeiter>Hans Geldfliech</Mitarbeiter>
</Kollegen>
<Ansprechpartner Telefon="030-435555" Ident="A_1">Otto
Moltoimportante</Ansprechpartner>
<Adresse>
<Strasse Gegend="teures Pflaster">Kurfürstendamm
5</Strasse>
<Ort>13453 Berlin</Ort>
</Adresse>
<Budget>40 000000</Budget>
<Kommentar>Alles wird gut</Kommentar>
</Gruppe>
<Gruppe>
<Gruppenname>Hamburger Verwaltungschaos </Gruppenname>
<Kollegen>
<Mitarbeiter Firma="Arthur der Kleine">Werner
Nordflut</Mitarbeiter>
<Mitarbeiter>Marina Meimportauncarajo</Mitarbeiter>
<Mitarbeiter>Peter Wessnich</Mitarbeiter>
</Kollegen>
<Ansprechpartner Ident="A_2">Ludwig Noresponsable</Ansprechpartner>
<Adresse>
<Strasse>An der Waterkant 15</Strasse>
<Ort>45555 Hamburg</Ort>
</Adresse>
<Kostenvoranschlag>30 000000</Kostenvoranschlag>
</Gruppe>
<Team>
<Name>Controlling</Name>
<Ansprechpartner>Werner Kostfix</Ansprechpartner>
<Telefon>030-4544332</Telefon>
</Team>
</Projekte>
Machen wir uns an einem einfachen Beispiel
klar, wie die Dinge zusammenhängen. Unser erstes XSLT-Stylesheet
sieht so aus:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- Durch die eingebauten Template Rules, werden alle
Elemente gezeigt (Attribute nicht !), auch wenn keine Templates
für das Element angegeben ist -->
<xsl:template match="/">
<!-- Der Punkt steht für den aktuellen Knoten -->
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>
Die XML-Datei speichern wir unter Unternehmensberatung
in dem Ordner ab, wo auch die heruntergeladene Datei saxon.exe
liegt. Das oben stehende XSLT-Stylesheet speichern wir in
dem gleichen Ordner unter dem Namen test.xsl ab. Es kann im
Einzelfalle Schwierigkeiten geben, Dateien mit der Endung
.xml bzw. .xsl abzuspeichern. Windows kennt diese Endung nicht
und fügt folglich ein .txt an, was wir im Moment nicht
brauchen. Um das zu verhindern, setzt man den Namen in Anführungsstriche,
man speichert also nicht Unternehmensberatung.xml ab sondern
"Unternehmensberatung.xml" und nicht test.xsl sondern
"test.xsl". Sind alle drei Dateien im gleichen Ordner,
wechseln wir mit der Dos Box (Eingabeaufforderung) in diesen
Ordner. Dies machen wir z.B. mit cd saxon wenn wir uns nach
öffnen der Box in c:\ befinden. Wenn nicht gehen wir
mit cd.. zuerst nach c:\ und geben dann cd saxon ein. Nehmen
wir an, der Ordner, wo wir alle Dateien abgelegt haben, heisst
saxon2. In diesem Fall wechseln wir in den Ordner saxon2 und
geben danach saxon Unternehmensberatung.xml test.xsl ein,
so dass wir folgendes Bild erhalten:
c:\saxon2>saxon Unternehmensberatung.xml
test.xsl
Anschliessend drücken wir die return-Taste. Wir sehen,
dass der komplette Inhalt, dass heisst der Text aller Knoten
ausgedruckt wird, allerdings nicht die Attribute. Schauen
wir uns das XSLT-Stylesheet im Detail an:
| 1) |
<?xml version="1.0" encoding="iso-8859-1"?> |
| 2) |
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> |
| 3) |
<!-- Durch die eingebauten Template
Rules, werden alle Elemente gezeigt (Attribute nicht !),auch
wenn keine Templates für das Element angegeben ist
--> |
| 4) |
<xsl:template match="/"> |
| 5) |
<xsl:value-of select="."/> |
| 6) |
</xsl:template> |
| 7) |
</xsl:stylesheet> |
| Aufbau
eines XSLT-Stylesheet |
|
Wie zu erkennen, muss ein XSLT-Stylesheet ebenfalls
den Ansprüchen an ein wohlgeformtes XML-Dokument genügen.
Jeder öffnende Tag muss also einen schliessenden Tag
haben, bzw. wenn das nicht der Fall ist, muss er mit dem Slash
( / ) geschlossen werden.
1) allerdings gehört noch nicht zum eigentlichen Stylesheet
und muss diesen Bedingungen nicht genügen. Hier wird
angegeben, um welche Version es sich handelt, also um 1.0,
wobei es im Moment nur diese gibt. Es folgt die Angabe des
Zeichensatzes, wir wählen iso-8859-1, weil wir dann Umlaute,
scharf s etc. verwendet können. In
2) geben wir mit xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
den Namespace an, dass heisst, dass alle Elemente, die zum
Stylesheet gehören das Präfix xsl haben, wobei wir
auch etwas anderes hätten wählen können. Entscheidend
ist jetzt die Zeile
4). Mit der Funktion template match greifen wir einen Knoten,
wobei innerhalb von template match festgelegt wird, was passieren
soll, wenn dieser Knoten gefunden wird. In unserem Falle soll
der Inhalt des Knotens angezeigt werden. Im Detail ist das
verblüffend, weil wir ja den kompletten Inhalt des gesamten
Dokumentes erhalten. Was passiert ist folgendes. Der Slash
/ besagt, dass an der Wurzel des Dokumentes gestartet werden
soll, danach erfolgt keine weitere XPATH-Spezifikation, (für
Details XPATH siehe Abfragen mit
XPATH), das heisst, wir erhalten den Knoten Projekte,
den Wurzelknoten des XML-Dokumentes und alle Kindknoten dieses
Knotens werden angezeigt, bzw. deren Text.
| Die
Funktion template match |
|
Was in einer template match Anweisung definiert
wird, wird so oft ausgeführt, wie Knoten dieses Typs
vorhanden sind.
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/Projekte/Gruppe">
Hallo
</xsl:template>
</xsl:stylesheet>
Wie oft druckt dieses Template hallo
? Zweimal, da es zwei Knoten dieses Typs gibt, also Knoten
mit dem Namen Gruppe. Genau genommen erhalten wir aber dies:
C:\saxon2>saxon Unternehmensberatung.xml
test.xsl
<?xml version="1.0" encoding="utf-8"?>
Hallo
Hallo
Controlling
Werner Kostfix
030-4544332
C:\saxon2>
Wir erhalten also auch noch alles unterhalb
des Knotens Team. Wie das ? Die Lösung ist einfach. Ist
für einen bestimmten Knoten kein Template definiert,
so wird er nicht etwa, wie man vermuten würde, nicht
angezeigt, sondern er wird angezeigt. Für den Knoten
Team haben wir aber kein Template definiert, folglich wird
es angezeigt. Wenn wir auf alle Knoten Mitarbeiter zugreifen
wollen, also zum Beispiel für jeden Knoten einmal hallo
sagen wollen, können wir das so machen:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates select="/Projekte/Gruppe/Kollegen/Mitarbeiter"/>
</xsl:template>
<xsl:template match="Mitarbeiter">
Hallo
</xsl:template>
</xsl:stylesheet>
Wie oft werden wir jetzt Hallo erhalten
? Nun, sechmal, weil wir sechs Mitarbeiter haben. Die Syntax
ist nun etwas komisch. Zuerst mal müssen wir uns klar
machen, dass sowas nicht zum gewünschten Ziel führt:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/Projekte/Gruppe/Kollegen/Mitarbeiter">
Hallo
</xsl:template>
</xsl:stylesheet>
Auch hier erhalten wir zwar sechsmal
hallo für die Mitarbeiter, aber zusätzlich noch
alle anderen Knoten (Gruppenname, Ansprechpartner, Adresse,
Budget etc.). Wer sich das genau anschauen will, kann es auch
fest auf die Platte drucken. Um dies zu realisieren ist ein
Parameter mehr nötig, nämlich die Datei, in die
gedruckt werden soll. Das sieht dann so aus:
C:\saxon2>saxon Unternehmensberatung.xml
test.xsl >banane.xml
Nachdem wir nun festgestellt haben, dass es
so nicht geht, gehen wir das Stylesheet mit dem wir unser
Ziel erreichen, nämlich für jedes Auftreten des
Knotens Mitarbeiter hallo zu sagen nochmal durch.
| 1) |
<?xml version="1.0" encoding="iso-8859-1"?> |
| 2) |
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> |
| 3) |
<xsl:template match="/"> |
| 4) |
<xsl:apply-templates select="/Projekte/Gruppe/Kollegen/Mitarbeiter"/> |
| 5) |
</xsl:template> |
| 6) |
<xsl:template match="Mitarbeiter"> |
| 7) |
Hallo |
| 8) |
</xsl:template> |
| 9) |
</xsl:stylesheet> |
Entscheidend sind die Zeilen 3) bis 4). Dadurch,
dass wir ein Template für den Wurzelknoten bestimmen,
ist garantiert, dass es keinen Knoten mehr gibt, für
den kein Template vorliegt, dass also nichts erscheint, was
nicht erscheinen soll. Innerhalb dieser Templatedefinition
rufen wir mit apply-templates eine neues Template auf, nämlich
das Template Mitarbeiter, dass dann wiederum Hallo sagt. Das
ist etwas ungewöhnlich, wenn man in den Kategorien einer
Programmiersprache denkt, weil es ja de facto eine Schleife
ist. Das gleiche kann man auch mit einem for-each Konstrukt
erreichen. Das sieht dann so aus:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="Projekte/Gruppe/Kollegen/Mitarbeiter">
Hallo
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Die Wirkung ist die gleiche, allerdings
ist die Syntax leichter zu lesen (für jedes (for each)
Element mit dem Namen Mitarbeiter mach das und das..), weil
es sich um eine "normale" foreach Schleife handelt.
Die Anweisung apply-templates ist aber flexibler, insbesondere
dann, wenn die Abfragen komplexer werden. Nehmen wir an, wir
wollen zuerst die Mitarbeiter einer Gruppe sehen und direkt
anschliessend den dazugehörigen Ansprechpartner. Dies
ist so nicht zu realisieren:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates select="/Projekte/Gruppe/Kollegen/Mitarbeiter"/>
<xsl:apply-templates select="/Projekte/Gruppe/Ansprechpartner"/>
</xsl:template>
<xsl:template match="Mitarbeiter">
Mitarbeiter: <xsl:value-of select="."/>
</xsl:template>
<xsl:template match="Ansprechpartner">
Ansprechpartner: <xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>
Das führt nicht zum Ziel. Zuerst
erhalten wir alle Mitarbeiter und anschliessend alle Ansprechpartner.
Wir wollen aber die ersten drei Mitarbeiter, dann den dazugehörigen
Ansprechpartner, dann wieder drei Mitarbeiter und dann wieder
den dazugehörigen Ansprechpartner. Was wir erhalten ist
das:
C:\saxon2>saxon Unternehmensberatung.xml
test.xsl
<?xml version="1.0" encoding="utf-8"?>
Mitarbeiter: Werner Lepinski
Mitarbeiter: Erika Saufwech
Mitarbeiter: Hans Geldfliech
Mitarbeiter: Werner Nordflut
Mitarbeiter: Marina Meimportauncarajo
Mitarbeiter: Peter Wessnich
Ansprechpartner: Otto Moltoimportante
Ansprechpartner: Ludwig Noresponsable
C:\saxon2>
Und das wollen wir nicht. Um zu erreichen
was wir wollen, brauchen wir dieses Stylesheet:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates select="/Projekte/Gruppe"/>
</xsl:template>
<xsl:template match="Gruppe">
<xsl:apply-templates select="Kollegen/Mitarbeiter"/>
<xsl:apply-templates select="Ansprechpartner"/>
</xsl:template>
<xsl:template match="Mitarbeiter">
Mitarbeiter: <xsl:value-of select="."/>
</xsl:template>
<xsl:template match="Ansprechpartner">
Ansprechpartner: <xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>
Das Programm macht dann was wir uns vorstellen.
Das Resultat sieht so aus:
<?xml version="1.0"
encoding="utf-8"?>
Mitarbeiter: Werner Lepinski
Mitarbeiter: Erika Saufwech
Mitarbeiter: Hans Geldfliech
Ansprechpartner: Otto Moltoimportante
Mitarbeiter: Werner Nordflut
Mitarbeiter: Marina Meimportauncarajo
Mitarbeiter: Peter Wessnich
Ansprechpartner: Ludwig Noresponsable
C:\saxon2>
Und das ist das, was wir wollen. Alternativ
hätte wir es auch so machen können:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates select="/Projekte/Gruppe"/>
</xsl:template>
<xsl:template match="Gruppe">
<xsl:for-each select="Kollegen/Mitarbeiter">
Mitarbeiter: <xsl:value-of select="."/>
</xsl:for-each>
Ansprechpartner: <xsl:value-of select="Ansprechpartner"/>
</xsl:template>
</xsl:stylesheet>
Die Logik dahinter ist einfach. Wir haben zwei
Knoten Gruppe und diese zwei Knoten rufen wir auf. Für
jeden Knoten Gruppe rufen wir dann die Mitarbeiter und die
Ansprechpartner auf.
| Zugriff
auf einen bestimmten Knoten aus einer Gruppe gleichnamiger
Knoten |
|
Wenn wir nur die zweite Gruppe erfassen
wollen, also nur die Mitarbeiter und den Ansprechpartner der
zweiten Gruppe, dann sieht das so aus:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates select="/Projekte/Gruppe[2]"/>
</xsl:template>
<xsl:template match="Gruppe">
<xsl:for-each select="Kollegen/Mitarbeiter">
Mitarbeiter: <xsl:value-of select="."/>
</xsl:for-each>
Ansprechpartner: <xsl:value-of select="Ansprechpartner"/>
</xsl:template>
</xsl:stylesheet>
Entscheidend ist hierbei die XPATH-Definition
/Projekte/Gruppe[2] mit der wir sofort auf den zweiten Knoten
zugreifen.
| Transformation
in ein HTML-Dokument |
|
Bis jetzt haben wir noch nicht viel transformiert,
wir haben uns lediglich damit beschäftigt, Zugriff auf
die einzelnen Knoten zu erhalten, was allerdings die conditio
sine qua non jeder Transformation ist. Wollen wir alle Mitarbeiter
jeder Gruppe rausfischen und dann dazu noch den Ansprechpartner
und das alles als HTML-Seite dann können wir das so machen:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html><head><title>Alles HTML</title></head><body>
<xsl:apply-templates select="/Projekte/Gruppe"/>
</body></html>
</xsl:template>
<xsl:template match="Gruppe">
<table>
<xsl:for-each select="Kollegen/Mitarbeiter">
<tr><td bgcolor="yellow">Mitarbeiter:
<xsl:value-of select="."/></td></tr>
</xsl:for-each>
<tr><td bgcolor="orange">Ansprechpartner:
<xsl:value-of select="Ansprechpartner"/></td></tr>
</table>
</xsl:template>
</xsl:stylesheet>
Wir können dieses XSLT-Stylesheet
dann auslösen. Am besten wir drucken es in eine HTML-Seite,
damit wir es uns anschliessend gleich mit dem Browser betrachten
können.
C:\saxon2>saxon Unternehmensberatung.xml test.xsl >test.html
Wir erhalten als Ergebnis: 
Um das zu verstehen, muss man wissen, welcher
Knoten wie oft aufgerufen wird. Der Knoten / also der Wurzelknoten,
wird nur ein einziges Mal aufgerufen. Folglich müssen
hier die Wurzelknoten des HTML-Dokumentes untergebracht werden.
Jede Gruppe ist ein eigener table, folglich müssen die
table-tags in das aufgerufene Template. Und die Zeilendefinitionen
an die Stelle, wo die Mitarbeiter aufgerufen werden.
| Das
Ergebnis der Transformation, den Ergebnisbaum, direkt
im Browser zeigen |
|
Man kann sich das auch direkt
im Browser anschauen, allerdings braucht man dann einen XSLT-Parser,
der clientseitig arbeitet. Microsoft stellt einen solchen
zur Verfügung, er heisst MSXML und kann hier
downgeloadet werden. Damit kann man dann XML-Dateien via XSL
transformieren und direkt im Browser anzeigen. Allerdings
ist der Ansatz reichlich sinnlos, weil z.B. Netscape-Browser
das nicht unterstützen. Will man den Ergebnisbaum, also
die transformierte XML-Datei direkt im Browser zeigen, ist
es günstiger man generiert sie auf dem Server und schickt
sie dann an den Client. Wie das geht, siehe Auslesen
eines XML Dokumentes mit XSLT unter Verwendung von Sablotron
und PHP und Auslesen eines
XML-Dokumentes mit XSLT unter Verwendung von Perl XSLT.
Der Vollständigkeit halber zeigen wir den Ansatz. Eigentlich
muss hierfür lediglich MSXML geladen sein und die XML-Datei
modifiziert werden.
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet href="test.xsl" type="text/xsl"?>
<Projekte>
<Gruppe>
<Gruppenname stand="abgeschifft">Berliner
Verwaltungsreform</Gruppenname>
<Kollegen>
<Mitarbeiter Firma="Preis Wasserhaus">Werner
Lepinski</Mitarbeiter>
<Mitarbeiter>Erika Saufwech</Mitarbeiter>
<Mitarbeiter>Hans Geldfliech</Mitarbeiter>
</Kollegen>
<Ansprechpartner Telefon="030-435555" Ident="A_1">Otto
Moltoimportante</Ansprechpartner>
<Adresse>
<Strasse Gegend="teures Pflaster">Kurfürstendamm
5</Strasse>
<Ort>13453 Berlin</Ort>
</Adresse>
<Budget>40 000000</Budget>
<Kommentar>Alles wird gut</Kommentar>
</Gruppe>
<Gruppe>
<Gruppenname>Hamburger Verwaltungschaos </Gruppenname>
<Kollegen>
<Mitarbeiter Firma="Arthur der Kleine">Werner
Nordflut</Mitarbeiter>
<Mitarbeiter>Marina Meimportauncarajo</Mitarbeiter>
<Mitarbeiter>Peter Wessnich</Mitarbeiter>
</Kollegen>
<Ansprechpartner Ident="A_2">Ludwig Noresponsable</Ansprechpartner>
<Adresse>
<Strasse>An der Waterkant 15</Strasse>
<Ort>45555 Hamburg</Ort>
</Adresse>
<Kostenvoranschlag>30 000000</Kostenvoranschlag>
</Gruppe>
<Team>
<Name>Controlling</Name>
<Ansprechpartner>Werner Kostfix</Ansprechpartner>
<Telefon>030-4544332</Telefon>
</Team>
</Projekte>
Wie deutlich zu erkennen, wurde lediglich
eine Zeile (die zweite) eingefügt. Man kann das jetzt
so abspeichern und im Browser aufrufen.
| Ermitteln
des Wertes eines Attributes |
|
Attribute sind in XPATH keine normalen
Knoten. Will man sie abrufen bedarf es einer speziellen Syntax.
Wenn wir alle Firmen rausfischen wollen, die ein Attribut
von Mitarbeiter sind, dann können wir das so machen:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates select="/Projekte/Gruppe/Kollegen/Mitarbeiter"/>
</xsl:template>
<xsl:template match="Mitarbeiter">
Hallo <xsl:value-of select="@Firma"/>
</xsl:template>
</xsl:stylesheet>
Was wir erhalten ist sowas
C:\saxon2>saxon Unternehmensberatung.xml
test.xsl
<?xml version="1.0" encoding="utf-8"?>
Hallo Preis Wasserhaus
Hallo
Hallo
Hallo Arthur der Kleine
Hallo
Hallo
C:\saxon2>
Das ist fast was wir wollen, aber nicht
ganz. Das wir für Mitarbeiter ein Template definiert
haben, wird dieses so oft aufgeführt, wie Mitarbeiter
vorhanden sind. Also sechs mal. Nur zwei davon haben aber
tatsächlich ein Attribut, die anderen laufen im Leerlauf.
Das Hallo kann man natürlich wegmachen, aber sauber ist
es nicht. Wir wollen nur die Attribute sehen. Korrekt ist
also so etwas:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="/Projekte/Gruppe/Kollegen/Mitarbeiter[@Firma]">
Hallo <xsl:value-of select="@Firma"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Wir erhalten dann was wir uns wünschen.
C:\saxon2>saxon Unternehmensberatung.xml
test.xsl
<?xml version="1.0" encoding="utf-8"?>
Hallo Preis Wasserhaus
Hallo Arthur der Kleine
C:\saxon2>
| Mehrere
Attribute eines Knotens anzeigen lassen |
|
Die Probleme können komplexer sein,
der Phantasie sind keine Grenzen gesetzt. Man kann den Namen
eines Mitarbeiters z.B. nur sehen wollen, wenn er zur Firma
XY gehört, man kann alle Attribute sehen wollen, man
kann sich für die Summe von Attributen interessieren
usw.usw. Um dies zu zeigen, modifizieren wir unser Beispiel.
Für eine detaillierte Darstellung siehe Abfragen
mit XPATH.
<?xml version="1.0" encoding="ISO-8859-1"?>
<Projekte>
<Gruppe>
<Gruppenname stand="abgeschifft">Berliner
Verwaltungsreform</Gruppenname>
<Kollegen>
<Mitarbeiter Firma="Preis Wasserhaus" Summe="90000">Werner
Lepinski</Mitarbeiter>
<Mitarbeiter Firma="Schitag Ernst und Jung" status="ausgeliehen"
Summe="20000">Erika Saufwech</Mitarbeiter>
<Mitarbeiter Summe="27000">Hans Geldfliech</Mitarbeiter>
</Kollegen>
<Ansprechpartner Telefon="030-435555" Ident="A_1">Otto
Moltoimportante</Ansprechpartner>
<Adresse>
<Strasse Gegend="teures Pflaster">Kurfürstendamm
5</Strasse>
<Ort>13453 Berlin</Ort>
</Adresse>
<Budget>40 000000</Budget>
<Kommentar>Alles wird gut</Kommentar>
</Gruppe>
<Gruppe>
<Gruppenname>Hamburger Verwaltungschaos </Gruppenname>
<Kollegen>
<Mitarbeiter Firma="Arthur der Kleine" Summe="30000">Werner
Nordflut</Mitarbeiter>
<Mitarbeiter Firma="Wertarbeit" status="ausgeliehen"
Summe="40000">Marina Meimportauncarajo</Mitarbeiter>
<Mitarbeiter Firma="Preis Wasserhaus" Summe="70000"
status="ausgeliehen">Peter Wessnich</Mitarbeiter>
</Kollegen>
<Ansprechpartner Ident="A_2">Ludwig Noresponsable</Ansprechpartner>
<Adresse>
<Strasse>An der Waterkant 15</Strasse>
<Ort>45555 Hamburg</Ort>
</Adresse>
<Kostenvoranschlag>30 000000</Kostenvoranschlag>
</Gruppe>
<Team>
<Name>Controlling</Name>
<Ansprechpartner>Werner Kostfix</Ansprechpartner>
<Telefon>030-4544332</Telefon>
</Team>
</Projekte>
Alle Mitarbeiter und alle dazugehörigen
Attribute ermitteln.
Will man sich alle Mitarbeiter zeigen lassen nebst alle Attributen,
dann kann man sowas machen.
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="/Projekte/Gruppe/Kollegen/Mitarbeiter">
Mitarbeiter <xsl:value-of select="."/>
<xsl:apply-templates select="./attribute::*"/>
</xsl:for-each>
</xsl:template>
<xsl:template match="attribute::*">
Attribut <xsl:value-of select="name()"/> Wert
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>
Das Ergebnis sieht dann so aus:
C:\saxon2>saxon Unternehmensberatung.xml
test.xsl
<?xml version="1.0" encoding="utf-8"?>
Mitarbeiter Werner Lepinski
Attribut Firma Wert Preis Wasserhaus
Attribut Summe Wert 90000
Mitarbeiter Erika Saufwech
Attribut Firma Wert Schitag Ernst und Jung
Attribut status Wert ausgeliehen
Attribut Summe Wert 20000
Mitarbeiter Hans Geldfliech
Attribut Summe Wert 27000
Mitarbeiter Werner Nordflut
Attribut Firma Wert Arthur der Kleine
Attribut Summe Wert 30000
Mitarbeiter Marina Meimportauncarajo
Attribut Firma Wert Wertarbeit
Attribut status Wert ausgeliehen
Attribut Summe Wert 40000
Mitarbeiter Peter Wessnich
Attribut Firma Wert Preis Wasserhaus
Attribut Summe Wert 70000
Attribut status Wert ausgeliehen
C:\saxon2>
| Aufsummieren
der Werte eines Attributes |
|
Will man die Summen des Attributs Summe
der Mitarbeiter erhalten (90000 + 20000+270000+300000+400000+700000=277000)
so kann man das mit diesem Stylesheet erreichen.
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:value-of select="sum(/Projekte/Gruppe/Kollegen/Mitarbeiter/attribute::Summe)"/>
</xsl:template>
</xsl:stylesheet>
| Auslesen
aller gleichnamigen Knoten in unterschiedlichen Gruppen |
|
Will man alle Ansprechpartner rausfischen,
dann ist das im obigen Beispiel nicht so einfach, da sich
einer davon ja auch in Team befindet. Eine XPATH-Abfrage nach
dem Schema /Projeke/Gruppe/Ansprechpartner funktionniert also
nicht. Es gibt aber eine einfache Möglichkeit, dies zu
lösen.
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="/Projekte/child::*/Ansprechpartner">
hallo <xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Man erhält dann alle Ansprechpartner.
Entscheidend ist hierbei die XPATH-Abfrage child::*. child
steht für die Kindknoten und mit dem * geben wir kund,
dass wir alle Kindknoten haben wollen, in unserem Falle also
Gruppe und Team. Alternativ hätte man auch mit oder (das
Zeichen für oder ist die Pipe |) arbeiten können.
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="/Projekte/Gruppe/Ansprechpartner
| /Projekte/Team/Ansprechpartner ">
hallo <xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Wir suchen jetzt entweder /Projekte/Gruppe/Ansprechpartner
oder /Projekte/Team/Ansprechpartner. Eine dritte Möglichkeit
wäre gewesen, mit einer Achse zu arbeiten, in diesem
Falle mit descendant. Diese Achse erfasst alle Knoten unterhalb
des Referenzknotens. Sie erfasst also auch die Enkelknoten
des Referenzknotens. Da sowohl die Kindknoten als auch die
Referenzknoten erfasst werden, erhalten wir sowohl so
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="/Projekte/descendant::*/Ansprechpartner">
hallo <xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
wie auch so
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="/descendant::*/Ansprechpartner">
hallo <xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
das richtige Ergebnis.
| Navigieren
mit Hilfe von Achsen |
|
XPATH kennt sogenannte Achsen,
die man nutzen kann, um innerhalb eines Dokumentes zu navigieren.
Betrachten wir das genauer.
Die Achse parent
Parent ermittelt den Elternknoten eines Knotens. Wenn wir
uns zum Beispiel dafür interessieren, ob der Ansprechpartner
innerhalb von Team oder Gruppe ist, dann können wir so
etwas tun.
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="/Projekte/child::*/Ansprechpartner">
Wo: <xsl:value-of select="name(parent::node())"/>
hallo <xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Die Achse following
Die Achse following erfasst die Knoten rechts vom aktuellen
Knoten. Würde man sich, aus bislang unbekannten Gründen,
dafür interessieren, wie der Schwesternknoten des Ansprechpartners
heisst, also Adresse, dann kann man sowas machen:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="/Projekte/child::*/Ansprechpartner">
Wo: <xsl:value-of select="name(following::*[position()=1])"/>
hallo <xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Zu berücksichtigen ist, dass following
nicht nur den Schwesternknoten schnappt, sondern auch alle
Knoten unter den Schwesternknoten, wenn der Schwesternknoten
also Adresse ist, dann wird die Strasse und der Ort mitgeschnappt.
Wir wollen aber nur den Namen von dem Schwesternknoten wissen,
aus wie gesagt unbekannten Gründen,
und fischen uns deshalb von allen Knoten (*) den raus, der
an erster Position steht. Auf diese Weise erhalten wir dann
sowas.
C:\saxon2>saxon Unternehmensberatung.xml
test.xsl
<?xml version="1.0" encoding="utf-8"?>
Wo: Adresse
hallo Otto Moltoimportante
Wo: Adresse
hallo Ludwig Noresponsable
Wo: Telefon
hallo Werner Kostfix
C:\saxon2>
Um noch mal zu verdeutlichen, dass die
Achse following den Schwesternknoten plus die Kindknoten des
Schwesternknoten rausfischt, lassen wir uns einen Kindknoten
des Schwesternknoten ausdrucken.
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="/Projekte/child::*/Ansprechpartner">
Wo: <xsl:value-of select="name(following::*[position()=2])"/>
hallo <xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Wir greifen jetzt auf den zweiten Knoten
von following zu, dies ist ein Kindknoten von Adresse, genau
genommen Strasse. Bei Telefon, die Schwester von Hans Kostfix,
klappt das nicht, weil Telefon gar keinen Kindknoten hat.
Das Ergebnis sieht so aus:
C:\saxon2>saxon Unternehmensberatung.xml
test.xsl
<?xml version="1.0" encoding="utf-8"?>
Wo: Strasse
hallo Otto Moltoimportante
Wo: Strasse
hallo Ludwig Noresponsable
Wo:
hallo Werner Kostfix
| Die
Achse following-sibling |
|
following-sibling erfasst alle Schwesternknoten,
allerdings ohne die Kindknoten dieser Schwestern.
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="/Projekte/child::*/Ansprechpartner">
Wo: <xsl:value-of select="name(following-sibling::*[position()=2])"/>
hallo <xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Das führt zum gleichen Ergebnis wie oben.
C:\saxon2>C:\saxon2>saxon
Unternehmensberatung.xml test.xsl
<?xml version="1.0" encoding="utf-8"?>
Wo: Budget
hallo Otto Moltoimportante
Wo: Kostenvoranschlag
hallo Ludwig Noresponsable
Wo:
hallo Werner Kostfix
C:\saxon2>
Unterschied zwischen der Achse following
und following-sibling
Der Unterschied zwischen following und following-sibling ist
nicht ohne weiteres einzusehen. following hat alle Schwesternknoten
rechts vom Startknoten und die Kindsknoten dieser Schwestern.
In unserem Falle ist der Starknoten Ansprechpartner. following-sibling
hat alle nur die Schwesternknoten, ohne die dazugehörigen
Kindsknoten. Machen wir uns die Unterschiede an einem Beispiel
klar.
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="/Projekte/child::*/Ansprechpartner">
Wo: <xsl:value-of select="name(following::*[position()=2])"/>
hallo <xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Hier werden die Kindsknoten miterfasst,
folglich ist der zweite Knoten ein Kind von Adresse, genaugenommen
Strasse. Wir erhalten das.
C:\saxon2>saxon Unternehmensberatung.xml
test.xsl
<?xml version="1.0" encoding="utf-8"?>
Wo: Strasse
hallo Otto Moltoimportante
Wo: Strasse
hallo Ludwig Noresponsable
Wo:
hallo Werner Kostfix
C:\saxon2>
Mit following-sibling steht an zweiter
Position aber nicht ein Kind des Schwesternknoten sondern
der nächste Schwesternknoten, also Budget in der ersten
Gruppe und Kostenvoranschlag in der zweiten Gruppe und nichts
in der dritten.
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="/Projekte/child::*/Ansprechpartner">
Wo: <xsl:value-of select="name(following-sibling::*[position()=2])"/>
hallo <xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Lösen wir das Stylesheet aus, erhalten
wir das.
C:\saxon2>saxon Unternehmensberatung.xml
test.xsl
<?xml version="1.0" encoding="utf-8"?>
Wo: Budget
hallo Otto Moltoimportante
Wo: Kostenvoranschlag
hallo Ludwig Noresponsable
Wo:
hallo Werner Kostfix
C:\saxon2>
Um mit following bis zur Position Budget
und Kostenvoranschlag vorzurücken müsste als Position
4 angegeben werden.
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="/Projekte/child::*/Ansprechpartner">
Wo: <xsl:value-of select="name(following::*[position()=4])"/>
hallo <xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Das führt dann zum gleichen Ergebnis
wie oben.
C:\saxon2>saxon Unternehmensberatung.xml
test.xsl
<?xml version="1.0" encoding="utf-8"?>
Wo: Budget
hallo Otto Moltoimportante
Wo: Kostenvoranschlag
hallo Ludwig Noresponsable
Wo:
hallo Werner Kostfix
C:\saxon2>
preceding bzw. preceding-sibling ist
das Gegenstück following bzw. following-sibling . preceding
erfasst alle Schwesternknoten die links vom Ausgangsknoten
stehen, sowie alle Kindknoten dieser Schwesternknoten. preceding-sibling
erfasst alle Schwesternknoten links vom Ausgangsknoten ohne
die Kindknoten dieser Schwesterknoten.
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="/Projekte/child::*/Ansprechpartner">
Wo: <xsl:value-of select="name(preceding::*[position()=1])"/>
hallo <xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Wir erhalten
C:\saxon2>saxon Unternehmensberatung.xml
test.xsl
<?xml version="1.0" encoding="utf-8"?>
Wo: Mitarbeiter
hallo Otto Moltoimportante
Wo: Mitarbeiter
hallo Ludwig Noresponsable
Wo: Name
hallo Werner Kostfix
C:\saxon2>
Das verblüfft uns erstmal. Der unmittelbar
links von Ansprechpartner liegende Schwesternknoten ist Kollege.
Mitarbeiter ist eine Hierarchistufe weiter unten und kein
Schwesternknoten. preceding zeigt die Knoten aber in umgekehrter
Dokumenten-Reihenfolge. Wenn also der Knoten Kollegen selber
wieder drei Knoten hat, dann ist er selber der vierte. Da
erwartete Ergebnis erhalten wir also so:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="/Projekte/child::*/Ansprechpartner">
Wo: <xsl:value-of select="name(preceding::*[position()=4])"/>
hallo <xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
| Ein
XML-Dokument mit XSLT modifizieren |
|
Im Prinzip, wenn das auch in der Literatur weit seltener
beschrieben wird, ist es genauso spannend ein XML-Dokument
zu verändern wie auszulesen.
Will man einem XML-Dokument ein Element hinzufügen,
so kann man das so machen:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/Projekte/Gruppe[2]/Kollegen">
<xsl:copy>
<xsl:element name="Mitarbeiter">
<xsl:attribute name="Firma">Treue Arbeit GmbH</xsl:attribute>
Franz Pingelkniek
</xsl:element>
<xsl:apply-templates select="/Projekte/Gruppe[2]/Kollegen/Mitarbeiter"
/>
</xsl:copy>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Diese Variante funktioniert sowohl mit dem saxon-Prozessor
als auch mit sablotron, Informationen zu sablotron siehe PHP-Handbuch.
Das Hinzufügen eines Elementes zerfällt in zwei
Prozesse.
| 1. |
Hinzufügen des Elementes an der richtigen
Stelle |
| 2. |
das ganze Dokument neu drucken. |
Mit /Projekte/Gruppe[2]/Kollegen/Mitarbeiter
wählen wir die Stelle aus, wo wir unser neues Element
einfügen wollen. Hierbei ist zu berücksichtigen,
dass der Befehl xsl:copy an dieser Stelle die bisherigen Inhalte
durch neue Inhalte ersetzt. Das heisst, die neuen Inhalte
müssen reingeschrieben werden und die alten wieder zurückkopiert
werden. Mit xsl:element fügen wird den neuen Inhalt ein
und setzen bei der Gelegenheit mit xsl:attribute auch ein
Attribut. Würden wir jetzt die alten Inhalte nicht zurückschreiben,
hätten wir nur das neue Element drin und die alten nicht
mehr. Deshalb kopieren wir mit /Projekte/Gruppe[2]/Kollegen/Mitarbeiter
die alten Inhalte zurück. Das heisst, der Kindknoten
von Mitarbeiter wurde komplett ausgetauscht. Anschliessend
wird im zweiten Teil das komplette, modifizierte, Dokument
neu geschrieben. Hierbei ist zu beachten, dass xsl:copy keine
Kindknoten mitkopiert. Wir müssen also jeden Knoten einzeln
erfassen und kopieren. Daher die Syntax @* und node(). Wir
können das mit dem gewohnten Schema auslösen
c:\saxon2\saxon Unternehmensberatung.xml
text.xsl >Unternehmensberatung2.xml
Unternehmensberatung2.xml ist dann das modifiezierte Dokument.
Es hat folgendes Aussehen.
<?xml version="1.0"
encoding="utf-8"?><Projekte>
<Gruppe>
<Gruppenname stand="abgeschifft">Berliner
Verwaltungsreform</Gruppenname>
<Kollegen>
<Mitarbeiter Firma="Preis Wasserhaus" Summe="90000">Werner
Lepinski</Mitarbeiter>
<Mitarbeiter Firma="Schitag Ernst und Jung" status="ausgeliehen"
Summe="20000">Erika Saufwech</Mitarbeiter>
<Mitarbeiter Summe="27000">Hans Geldfliech</Mitarbeiter>
</Kollegen>
<Ansprechpartner Telefon="030-435555" Ident="A_1">Otto
Moltoimportante</Ansprechpartner>
<Adresse>
<Strasse Gegend="teures Pflaster">Kurfürstendamm
5</Strasse>
<Ort>13453 Berlin</Ort>
</Adresse>
<Budget>40 000000</Budget>
<Kommentar>Alles wird gut</Kommentar>
</Gruppe>
<Gruppe>
<Gruppenname>Hamburger Verwaltungschaos </Gruppenname>
<Kollegen>
<Mitarbeiter Firma="Treue Arbeit GmbH">Franz
Pingelkniek</Mitarbeiter>
<Mitarbeiter Firma="Arthur der Kleine" Summe="30000">Werner
Nordflut</Mitarbeiter>
<Mitarbeiter Firma="Wertarbeit" status="ausgeliehen"
Summe="40000">Marina Meimportauncarajo</Mitarbeiter>
<Mitarbeiter Firma="Preis Wasserhaus" Summe="70000"
status="ausgeliehen">Peter Wessnich</Mitarbeiter>
</Kollegen>
<Ansprechpartner Ident="A_2">Ludwig Noresponsable</Ansprechpartner>
<Adresse>
<Strasse>An der Waterkant 15</Strasse>
<Ort>45555 Hamburg</Ort>
</Adresse>
<Kostenvoranschlag>30 000000</Kostenvoranschlag>
</Gruppe>
<Team>
<Name>Controlling</Name>
<Ansprechpartner>Werner Kostfix</Ansprechpartner>
<Telefon>030-4544332</Telefon>
</Team>
Wie deutlich zu erkennen, wurde in der zweiten
Gruppe der Franz Pingelkniek eingefügt.
Um ein Element zu ändern brauchen
wir im ersten Teil kein xsl:copy. xsl:element überschreibt
das angesteuerte Element. Um ein Element zu ändern,
z.B. den Peter Wessnich zu ersetzen durch den Franz Pingelkniek,
brauchen wir etwas in der Art:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/Projekte/Gruppe[2]/Kollegen/Mitarbeiter[3]">
<xsl:element name="Mitarbeiter">
<xsl:attribute name="Firma">Treue Arbeit GmbH</xsl:attribute>
Franz Pingelkniek
</xsl:element>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Bei dieser Variante wird einfach der dritte
Mitarbeiter durch ein neues Element ersetzt.
Dies ist der denkbar einfachste Fall. Wir schreiben
ein Template für das zu löschende Element, aber
innerhalb dieses Templates machen wir nix, dann ist es eben
weg.
Mit dem Skript unten zum Beispiel wird der Mitarbeiter Marina
Meimportauncarajo gelöscht.
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/Projekte/Gruppe[2]/Kollegen/Mitarbeiter[2]">
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
| Ein
Element innerhalb des Dokumentes umpflanzen |
|
Man muss sich im Klaren sein, dass die
einfache Lösung, die einem sofort einfällt, nicht
funktioniert. Wir wollen den zweiten
Mitarbeiter in der ersten Gruppe, die Erika Saufwech, aus
der ersten Gruppe ausschneiden und sie in die zweite Gruppe
setzten.
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/Projekte/Gruppe[2]/Kollegen">
<xsl:copy>
<xsl:apply-templates select="/Projekte/Gruppe[1]/Kollegen/Mitarbeiter[2]"/>
<xsl:apply-templates select="/Projekte/Gruppe[2]/Kollegen/Mitarbeiter"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/Projekte/Gruppe[1]/Kollegen/Mitarbeiter[2]">
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Zwar wird das zweite Element, die Erika
Saufwech gelöscht und die Elemente von der Gruppe zwei
werden auch zurückkopiert, aber die Erika Saufwech wird
nicht reinkopiert. Warum ist irgendwie unklar. Problemlos
läßt sich der dritte und der erste Mitarbeiter
in die zweite Gruppe einbauen, aber das ist leider nicht das
was wir wollen, wir wollen ja den Mitarbeiter, den wir oben
gelöscht haben unten einbauen. Da diese einfache Lösung
nicht funktioniert, brauchen wir so was in der Art:
<xsl:template match="/Projekte/Gruppe[2]/Kollegen">
<xsl:copy>
<xsl:apply-templates select="/Projekte/Gruppe[2]/Kollegen/Mitarbeiter"/>
<xsl:apply-templates select="/Projekte/Gruppe[1]/Kollegen/Mitarbeiter[2]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/Projekte/Gruppe[1]/Kollegen">
<xsl:copy>
<xsl:for-each select="Mitarbeiter">
<xsl:if test="position()!=2">
<xsl:apply-templates select="."/>
</xsl:if>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Bei dieser Lösung werden die Mitarbeiter
beider Gruppen komplett neu geschrieben. Wir kopieren also
erstmal die Erika Saufwech plus die originären Mitarbeiter
in den Knoten Mitarbeiter der ersten Gruppe. Anschliessend
beschreiben wir den Knoten Mitarbeiter der ersten Gruppe,
dass heisst wir kopieren in diesen Knoten alle Knoten ausser
dem Knoten 2, der ja die Erika Saufwech ist, die wir in der
ersten Gruppe nicht mehr haben wollen.
| Elemente
sortieren mit XSLT |
|
Elemente sortieren ist relativ unproblematisch,
zu mindestens in einfachen Fällen, und geht so:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="/Projekte/Gruppe/Kollegen/Mitarbeiter">
<xsl:sort select="."/>
Hallo <xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
xsl:sort kann Attribute haben, nämlich
ascending, was der default-Wert ist, oder
descending, absteigend, was bedeutet, dass die Reichenfolge
ZYXWVU ist.
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="/Projekte/Gruppe/Kollegen/Mitarbeiter">
<xsl:sort select="." order="descending"/>
Hallo <xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Es kann interessant sein, die Datensätze
bei der Ausgabe zu nummerieren. Das kann man dann so machen.
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="/Projekte/Gruppe/Kollegen/Mitarbeiter">
<xsl:sort select="." order="descending"/>
Hallo <xsl:value-of select="."/>(<xsl:value-of
select="position()"/>)
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Bei Verwendung von number, kann man die
Nummerierung feiner steuern, also z.B. mit römischen
Ziffern, Buchstaben etc. Das sieht dann so aus:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="/Projekte/Gruppe/Kollegen/Mitarbeiter">
Hallo <xsl:value-of select="."/>(<xsl:number
format="I" />)
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Leider entspricht das Ergebnis nicht
unseren Erwartungen. Wir erhalten so was:
C:\saxon2>saxon Unternehmensberatung.xml
test.xsl
<?xml version="1.0" encoding="utf-8"?>
Hallo Werner Lepinski(I)
Hallo Erika Saufwech(II)
Hallo Hans Geldfliech(III)
Hallo Werner Nordflut(I)
Hallo Marina Meimportauncarajo(II)
Hallo Peter Wessnich(III)
C:\saxon2>
Das heisst jede Gruppe wird für
sich nummeriert. Will man tatsächlich alle durchnummerieren,
kann man sowas machen:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="/Projekte/Gruppe/Kollegen/Mitarbeiter">
Hallo <xsl:value-of select="."/>(<xsl:number
format="I" level="any" />)
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Will man nun so etwas machen, wie in
Büchern üblich , also Kapitel und dann Unterkapitel,
läßt sich auch das mit number realisieren.
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates select="/Projekte/Gruppe"/>
</xsl:template>
<xsl:template match="Gruppe">
Gruppe: <xsl:number format="1"/> <xsl:value-of
select="node()"/>
<xsl:apply-templates select="Kollegen/Mitarbeiter"/>
</xsl:template>
<xsl:template match="Kollegen/Mitarbeiter">
<xsl:for-each select=".">
Hallo <xsl:value-of select="."/>(<xsl:number
format="I" />)
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Man erhält dann, wenn das Beispiel
von oben zugrunde liegt, das:
C:\saxon2>saxon Unternehmensberatung.xml
test.xsl
<?xml version="1.0" encoding="utf-8"?>
Gruppe: 1
Hallo Werner Lepinski(I)
Hallo Erika Saufwech(II)
Hallo Hans Geldfliech(III)
Gruppe: 2
Hallo Werner Nordflut(I)
Hallo Marina Meimportauncarajo(II)
Hallo Peter Wessnich(III)
C:\saxon2>
Bedingte
Anweisungen xsl:if und xsl:choose
|
|
Wir haben oben, bei
der Erklärung von xsl:copy die if Bedingung schon verwendet,
ohne sie genauer zu erklären. Im Grunde gibt es auch
nicht viel zu erklären, sie entspricht weitgehend der
if Bedingung, wie man sie aus allen Programmiersprachen kennt.
Weitgehend identisch sind auch die Operatoren. Will man z.B.
alle Mitarbeiter, also auch die Anprechpartner, sehen, die
das Attribut Firma besitzen, kann man so was in der Art machen:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates select="/Projekte/Gruppe/Kollegen/Mitarbeiter
| /Projekte/Gruppe/Ansprechpartner"/>
</xsl:template>
<xsl:template match="Mitarbeiter | Ansprechpartner">
<xsl:if test="name(./@*)='Firma'">
Hallo <xsl:value-of select="."/> (<xsl:value-of
select="name(./@*)"/> ist <xsl:value-of select="./@Firma"/>)
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Die Attribute sind Kindknoten des aktuellen
Knotens, folglich kann man mit ./@* alle Attribute abgreifen.
Mit name(./@*) hat man dann den Namen dieses Knotens. Das
ist soweit ganz nett und funktioniert in diesem Beispiel auch,
wir erhalten etwas, was richtig aussieht:
C:\saxon2>saxon Unternehmensberatung.xml
test.xsl
<?xml version="1.0" encoding="utf-8"?>
Hallo Werner Lepinski (Firma ist Preis Wasserhaus)
Hallo Erika Saufwech (Firma ist Schitag
Ernst und Jung)
Hallo Werner Nordflut (Firma ist Arthur
der Kleine)
Hallo Marina Meimportauncarajo (Firma
ist Wertarbeit)
Hallo Peter Wessnich (Firma ist Preis
Wasserhaus)
C:\saxon2>saxon Unternehmensberatung.xml
test.xsl
Leider ist es falsch. Es funktionniert
nur, weil in diesem Beispiel Firma immer das erste Attribut
ist, wir hätten im übrigen auch @Firma schreiben
können. Es wird nur das erste Attribut aufgerufen, weil
wir uns sozusagen in einer for-Schleife befinden, und folglich
@* auch nur mit dem ersten Element aufgerufen wird. Ist Firma
nicht das erste Attribut, bleibt es hier unbekannt. Will man
ein Stylesheet schreiben, dass das oben gestellte Problem
unabhängig von der Position des Attributs löst,
dann braucht man eher sowas:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates select="/Projekte/Gruppe/Kollegen/Mitarbeiter
| /Projekte/Gruppe/Ansprechpartner"/>
</xsl:template>
<xsl:template match="Mitarbeiter | Ansprechpartner">
<xsl:variable name="Name_des_Mitarbeiters"><xsl:value-of
select="."/></xsl:variable>
<xsl:for-each select="./@*">
<xsl:if test="name()='Firma'">
Hallo <xsl:value-of select="$Name_des_Mitarbeiters"/>
(<xsl:value-of select="name()"/> ist <xsl:value-of
select="."/>)
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Wer testen will, ob es tatsächlich
funktioniert, der muss die Datei Unternehmensberatung.xml,
auf der alle Beispiele aufbauen, so modifizieren, dass das
Attribut Firma nicht mehr an erster Stelle steht. Um die Vorgehensweise
zu verstehen, muss man sehen, dass wir innerhalb von xsl:for-each
keine Möglichkeit mehr haben, auf das Attribut zuzugreifen.
Der Kontextknoten innerhalb von xsl:if ist der Attributknoten
und nicht der Name des Mitarbeiters. Folglich müssen
wir den Namen des Mitarbeiters an einer Stelle abgreifen,
wo wir noch Zugriff haben, ihn dann in einer Variable sichern
und ihn aus dieser Variablen bei Bedarf wieder rausfischen.
Auf jeden Fall, haben wir jetzt xsl:if erlebt.
Eine
Bedingung von mehreren ist richtig: xsl:choose |
|
xsl:choose macht das, was
bei Perl if in Verbindung mit elsif macht, respektive in anderen
Programmiersprachen die case-Anweisung. Mit xsl:choose kann
man mehrere Bedingungen setzten und für jede Bedingung
isoliert festlegen, was geschehen soll, wenn die entsprechende
Bedingung zutrifft. z.B. könnte man bei der Transformation
wollen, dass der Ansprechpartner in rot und die normalen Mitarbeiter
blau erscheinen. Das sieht dann so aus:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates select="/Projekte/Gruppe/Kollegen/Mitarbeiter
| /Projekte/Gruppe/Ansprechpartner"/>
</xsl:template>
<xsl:template match="Mitarbeiter
| Ansprechpartner">
<xsl:choose>
<xsl:when test="name()='Mitarbeiter'">
Hallo <font color="blue"><xsl:value-of
select="."/></font>
</xsl:when>
<xsl:when test="name()='Ansprechpartner'">
Hallo <font color="red"><xsl:value-of select="."/></font>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Funktionen
zum Arbeiten mit Zeichenketten |
|
Ausgefeilte Funktionnen
zum Suchen und Bearbeiten von Zeichenketten gibt es in XSLT
nicht. Das liegt wohl auch daran, dass man solche in der Regel
nicht braucht. Ausgefeilte Funktionen zum Bearbeiten von Zeichenketten,
wie etwa regular expression, braucht man eigentlich nur dann,
wenn Texte nur sehr schwach strukturiert sind. Der Witz bei
XML besteht aber gerade darin, dass sich sehr klar strukturieren
lässt, die Daten also nie so chaotisch vorliegen. Einige
Funktionen gibt es aber auch in XSLT, die werden wir nun kurz
vorstellen.
Die
Funktion string-length |
|
Die Funktion string-length
ermittelt die Länge einer Zeichenkette.
Wenn wir ermitteln wollen wieviele Buchstaben inklusiv Leerzeichen
ein Zeichenketten hat, können wir sowas in der Art machen:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates select="/Projekte/Gruppe/Kollegen/Mitarbeiter"/>
</xsl:template>
<xsl:template match="Mitarbeiter">
Länge <xsl:value-of select="string-length(.)"/>
</xsl:template>
</xsl:stylesheet>
Will man die Länge des Knotennamens
ermitteln, kann man sowas machen.
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates select="/Projekte/Gruppe/Kollegen/Mitarbeiter"/>
</xsl:template>
<xsl:template match="Mitarbeiter">
Länge <xsl:value-of select="string-length(name())"/>
</xsl:template>
</xsl:stylesheet>
Will man ermitteln, wieviele Buchstaben das
Atrribut Firma hat, kann man so was machen:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates select="/Projekte/Gruppe/Kollegen/Mitarbeiter/attribute::Firma"/>
</xsl:template>
<xsl:template match="Mitarbeiter/attribute::Firma">
Länge <xsl:value-of select="string-length(.)"/>
</xsl:template>
</xsl:stylesheet>
Will man sich nur die Mitarbeiter anzeigen
lassen, die mehr weniger als 16 Buchstaben haben, kann man
das so machen:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates select="/Projekte/Gruppe/Kollegen/Mitarbeiter"/>
</xsl:template>
<xsl:template match="Mitarbeiter">
<xsl:if test="string-length(.)<='15'">
Name <xsl:value-of select="."/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Wir erhalten
C:\saxon2>saxon Unternehmensberatung.xml
test.xsl
<?xml version="1.0" encoding="utf-8"?>
Name Werner Lepinski
Name Erika Saufwech
Name Hans Geldfliech
Name Werner Nordflut
Name Peter Wessnich
C:\saxon2>
Wie deutlich zu erkennen, muss das <=
anstelle von <= stehen bzw. < = anstelle von >=
stehen. Im übrigen sind die üblichen Operatoren
gültig. Will man z.B. alle Mitarbeiter sehen, die nicht
exakt 15 Zeichen haben, kann man sowas machen:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates select="/Projekte/Gruppe/Kollegen/Mitarbeiter"/>
</xsl:template>
<xsl:template match="Mitarbeiter">
<xsl:if test="string-length(.)!='15'">
Name <xsl:value-of select="."/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Man erhält dann so was:
C:\saxon2>saxon Unternehmensberatung.xml
test.xsl
<?xml version="1.0" encoding="utf-8"?>
Name Erika Saufwech
Name Marina Meimportauncarajo
Name Peter Wessnich
C:\saxon2>
Mit concat lassen sich Zeichenketten
zu einer grossen Zeichenkette verbinden, das sieht dann so
aus:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates select="/Projekte/Gruppe/Kollegen/Mitarbeiter"/>
</xsl:template>
<xsl:template match="Kollegen/Mitarbeiter">
Hallo <xsl:value-of select="concat(node(),' Firma
',@Firma)"/>
</xsl:template>
</xsl:stylesheet>
Mit der Funktion contains kann man überprüfen,
ob eine Zeichenkette in einer anderen enthalten ist. Mit contains
können wir uns z.B. alle Mitarbeiter anzeigen lassen,
die als Vornamen Werner haben (Werner Lepinski und Werner
Nordflut).
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates select="/Projekte/Gruppe/Kollegen/Mitarbeiter"/>
</xsl:template>
<xsl:template match="Kollegen/Mitarbeiter">
<xsl:if test="contains(node(),'Werner')">
Hallo <xsl:value-of select="."/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Mit der Funktion star-with
kan man prüfen, ob eine Zeichenkette mit einer bestimmten
Zeichenkette beginnt. Wollen wir z.B. alle Mitarbeiter, deren
Namen mit P anfängt (Peter Wessnich) dann können
wir sowas machen:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates select="/Projekte/Gruppe/Kollegen/Mitarbeiter"/>
</xsl:template>
<xsl:template match="Kollegen/Mitarbeiter">
<xsl:if test="starts-with(node(),'P')">
Hallo <xsl:value-of select="."/>
</xsl:if>
</xsl: |