Grundlagen
Bei XPath (XML Path Language, zu Deutsch XML Pfadsprache) handelt es sich um eine Abfragesprache, die dazu verwendet wird, Knoten innerhalb eines XML-Dokuments zu adressieren. Verwendung findet XPath dabei vor allem in der Transformationssprache XSLT, kann jedoch auch in anderen Programmiersprachen (z. B. JavaScript oder C#) für die Adressierung von Bestandteilen des XML-Baums verwendet werden.
Die mit XPath definierten Ausdrücke werden nicht in extra Dateien abgespeichert, sondern in anderen Dokumenten bzw. in Skripten eingebettet. Die Sprache XPath wurde vom W3C entworfen und dient zudem als Grundlage für weitere Sprachen.
In diesem Thema werden wir verschiedene Fachbegriffe (wie z. B. Knoten und Achse) sowie die Schlüsselwörter und den Syntax von XPath erläutern.
Knoten
In Bezug auf XML und XPath werden Sie oft den Begriff Knoten (engl. node) hören. Doch was genau ist ein Knoten? Unter einem Knoten versteht man einen Teil eines XML-Dokuments oder auch das ganze XML-Dokument. Dabei stehen Knoten in enger Verbindung mit der Baumstruktur von XML. Bei Knoten wird zwischen ein paar unterschiedlichen Typen unterschieden:
- Wurzelknoten (engl. root node, auch Dokumentknoten genannt): Dabei handelt es sich um den abstrakten Ursprung des XML-Dokuments. Der Wurzelknoten repräsentiert das ganze Dokument und darf nicht mit dem Wurzelelement verwechselt werden.
- Elementknoten (engl. element node): ein beliebiges Element.
- Textknoten (engl. text node): ein Text, welcher einem Element untergeordnet ist.
- Attributknoten (engl. attribute node): ein beliebiges Attribut eines Elements.
- Kommentarknoten (engl. comment node): ein beliebiger Kommentar.
- Namensraumknoten (engl. namespace node): eine beliebige Namensraumangabe eines Elements oder Attributs.
- Verarbeitungsanweisungsknoten (engl. processing instruction node): ein XML-Verarbeitungshinweis (die XML-Deklaration zählt dabei nicht dazu).
Achsen
Für die Pfade, welche nachher genutzt werden, um im Dokument zu navigieren bzw. Knoten zu adressieren, werden sogenannte Achsen (engl. axis) benötigt. Eine Achse spezifiziert dabei die Beziehung des gewünschten Knotens (also dem Knoten, welchen Sie mittels XPath adressieren möchten) zum aktuellen Knoten (auch als Kontextknoten bezeichnet). Einige Achsen adressieren nur einzelne Knoten, wohingegen mit anderen Achsen auch mehrere Knoten adressiert werden können. Die folgende Tabelle zeigt die verschiedenen Achsen (Name und Kurz-Notation) sowie die Knoten, die damit selektiert werden:
Name | Kurz-Notation | selektierte Knoten |
---|---|---|
/ | / | Wurzelknoten |
child | (nicht notwendig) | direkt untergeordnete Knoten (Kindknoten) |
self | . | aktuelle Knoten (Kontextknoten) |
parent | .. | direkt übergeordneter Knoten (Elternknoten) |
descendant | .// | alle untergeordnete Knoten |
descendant-or-self | alle untergeordnete Knoten sowie der aktuelle Knoten | |
ancestor | alle übergeordnete Knoten | |
ancestor-or-self | alle übergeordneten Knoten sowie der aktuelle Knoten | |
following | alle nachfolgende Knoten (ohne Kindknoten) | |
following-sibling | alle nachfolgende Knoten (ohne Kindknoten), die den gleichen Elternknoten haben | |
preceding | alle vorangehende Knoten (ohne alle Elternknoten) | |
preceding-sibling | alle vorangehende Knoten (ohne alle Elternknoten), die den gleichen Elternknoten haben | |
attribute | @ | Attributknoten |
namespace | Namensraumknoten |
Prädikate
Ein Prädikat (engl. predicate) wird in XPath dazu verwendet, mit Hilfe von Ausdrücken (i. d. R. handelt es sich dabei um
Bedingungen) die Selektierung von Knoten weiter einzuschränken. Ein typisches Beispiel: Sie möchten alle Elemente mit
dem Namen x
selektieren, aber nur, sofern diese über das Attribut y
verfügen. Die Notation von
Prädikaten erfolgt in den eckigen Klammern [
und ]
.
Innerhalb der Prädikate können Sie verschiedene Operatoren verwenden. Dazu zählen die mathematischen Operatoren +
(Addition), -
(Subtraktion), *
(Multiplikation), div
(Division) und mod
(Modulo, Divisionsrest), aber auch die logischen Operatoren and
(und) und or
(oder) sowie die
Vergleichsoperatoren =
(gleich), !=
(ungleich), <
(kleiner als), <=
(kleiner-gleich), >
(größer als) und >=
(größer-gleich).
Eine weitere beliebte Verwendung von Prädikaten ist die Adressierung eines einzelnen Knotens mit Hilfe des Indexes. Bei einem Index handelt es sich um eine laufende Nummer. Bei der Notation wird der Index direkt als Zahl innerhalb der eckigen Klammern angegeben: 1 = erster Knoten, 2 = zweiter Knoten, etc..
Oft werden Prädikate aber auch in Verbindung mit XPath-Funktionen (dazu mehr im nächsten Thema) verwendet. Dadurch ist z. B. die Adressierung des letzten Knotens, das Modifizieren von Zahlen und Zeichenketten und vieles mehr möglich.
Pfade
Ein XPath-Ausdruck (also der Pfad, welcher zur Adressierung von Knoten verwendet wird) setzt sich aus einem oder mehreren
Lokalisierungsschritt(en) zusammen. Ein Lokalisierungsschritt besteht wiederum aus weiteren 3 Teilen: der Achse,
einem Knotentest und bei Bedarf aus einem oder mehreren Prädikat(en). Wird bei der Angabe der Achse der Achsenname verwendet,
so muss der Achsenname vom Knotentest mit Hilfe der Zeichen ::
(Doppel-Doppelpunkt) getrennt werden. Wird die
verkürzte Notation verwendet, so fällt der Doppel-Doppelpunkt weg. Der Knotentest ist letztendlich nichts anderes als
der Name des Knotens. Alternativ kann hier auch mit dem sogenannten Wildcard-Zeichen *
gearbeitet werden.
Das Wildcard-Zeichen dient dabei als Platzhalter für den Namen. Mit *
können somit also alle Knoten (ausgehend
von dem aktuellen Knoten und abhängig von der Achse) unabhängig vom Namen selektiert werden. Lokalisierungsschritte werden
mit Hilfe des Schrägstrichs /
voneinander getrennt.
Beispiele
Wie Sie sicherlich bemerkt haben, war dieses Thema sehr theorielastig. Daher wollen wir uns das theoretische Wissen nun noch an ein paar Praxisbeispielen verdeutlichen. In den Beispielen nutzen wir folgendes XML-Dokument:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> <adressbuch> <kontakt geschlecht="m" alter="17"> <name>Max Mustermann</name> <strasse>Musterstraße 123</strasse> <ort>12345 Musterstadt</ort> </kontakt> <kontakt geschlecht="w" alter="24"> <name>Maria Musterfrau</name> <strasse>Musterstraße 456</strasse> <ort>67890 Musterhafen</ort> </kontakt> <kontakt geschlecht="m" alter="53"> <name>Peter Müller</name> <strasse>Hauptstraße 29</strasse> <ort>27946 Programmierstadt</ort> </kontakt> </adressbuch>
Eine kurze Erklärung dazu: adressbuch
ist das Wurzelelement (nicht der Wurzelknoten), welchem 3 Kindelemente
(child
) mit dem Namen kontakt
untergeordnet sind. Das Elternelement (parent
)
der kontakt
-Elemente ist daher das Element adressbuch
. Jedes kontakt
-Element besitzt
das Attribut geschlecht
, welches angibt, ob es sich um eine männliche oder weibliche Person handelt, und das
Attribut alter
, welche das Alter der Person spezifiziert. Genauere Angaben zur Person werden über die Elemente
name
, strasse
(eigentlich straße
, jedoch sollten Sonderzeichen in Knotennamen vermieden
werden) und ort
festgelegt. Zu den übergeordneten Knoten (ancestor
) des Elements ort
,
gehört daher kontakt
und adressbuch
. Untergeordnete Knoten (descendant
) aus Sicht
des Elements adressbuch
sind die kontakt
-Elemente sowie deren ungeordneten Elemente name
,
strasse
und ort
.
Nun wollen wir uns jedoch ein paar XPath-Ausdrücken widmen. Folgender Ausdruck selektiert alle kontakt
-Elemente:
/child::adressbuch/child::kontakt
Die weiter oben angesprochene verkürzte Notation würde dazu wie folgt aussehen:
/adressbuch/kontakt
Möchten wir z. B. nur das erste kontakt
-Element selektieren, so notieren wir den folgenden Ausdruck:
/child::adressbuch/child::kontakt[1]
In der Regel wird die ausführliche Notation nur selten verwendet. In den nächsten Beispielen entspricht der erste Ausdruck immer der ausführlichen Notation und der zweite Ausdruck der verkürzten Notation. Hier die verkürzte Notation zum Ausdruck von oben:
/adressbuch/kontakt[1]
Wollen wir alle kontakt
-Elemente selektieren, die über das Attribut geschlecht
verfügen (unabhängig
vom Wert), dann könnten wir folgenden Ausdruck verwenden:
/child::adressbuch/child::kontakt[attribute::geschlecht]
/adressbuch/kontakt[@geschlecht]
Es ist aber auch möglich, mittels Prädikat mehrere Attribute „auf einmal“ abzufragen. Im folgenden Beispiel werden alle
kontakt
-Elemente selektiert, die über das Attribut geschlecht
und alter
verfügen:
/child::adressbuch/child::kontakt[attribute::geschlecht and attribute::alter]
/adressbuch/kontakt[@geschlecht and @alter]
Stellen Sie sich vor, Sie möchten als nächstes alle kontakt
-Elemente selektieren, aber nur dann, wenn der Wert des
Attributs geschlecht
m
ist:
/child::adressbuch/child::kontakt[attribute::geschlecht='m']
/adressbuch/kontakt[@geschlecht='m']
Mit dem Ausdruck von oben erhalten Sie nun die kompletten kontakt
-Knoten aller männlichen Kontakte Ihres Adressbuchs.
Es ist aber auch möglich, nur die Namen dieser Personen herauszufinden, indem wir das name
-Element selektieren:
/child::adressbuch/child::kontakt[attribute::geschlecht='m']/child::name
/adressbuch/kontakt[@geschlecht='m']/name
Bei einer Selektierung mit dem Ausdruck von oben werden Sie feststellen (sofern Sie den Ausdruck getestet haben), dass das
Ergebnis nicht Max Mustermann
und Peter Müller
ist, sondern <name>Max Mustermann</name>
und <name>Peter Müller</name>
. In einigen Fällen kann es jedoch notwendig sein, nicht (wie hier) das
name
-Element zu selektieren, sondern dessen Textinhalt (also den Textknoten). Für diesen Fall wird als Knotentest
text()
angegeben. Dadurch kann dann ein Textknoten (der nicht mittels eines Namens selektiert werden kann)
adressiert werden. Dies sieht dann bspw. so aus:
/child::adressbuch/child::kontakt[attribute::geschlecht='m']/child::name/child::text()
/adressbuch/kontakt[@geschlecht='m']/name/text()
Attribute, deren Inhalt ein Zahlenwert ist, können auch mit dem Kleiner-Als- und Größer-Als-Operator verglichen werden. Im folgenden Beispiel werden nur Kontakte selektiert die mindestens 18 Jahre alt sind:
/child::adressbuch/child::kontakt[attribute::alter >= 18]
/adressbuch/kontakt[@alter >= 18]
Zum Schluss noch ein Beispiel mit dem Wildcard-Zeichen: Mit dem folgenden Ausdruck selektieren wir alle Kindknoten (unabhängig
von deren Namen) der kontakt
-Elemente:
/child::adressbuch/child::kontakt/child::*
/adressbuch/kontakt/*
Wichtig: In diesen Beispielen wurde immer mit absoluter Adressierung gearbeitet. In Sprachen wie XSLT wird dies nur selten verwendet, da man sich hier Stück für Stück durch das Dokument durcharbeitet. Für die Beispiele hier hätten wir jedoch dann immer dazu schreiben müssen, welcher Knoten der Kontextknoten, also der „aktuelle“ Knoten ist, um das Beispiel überhaupt nachvollziehen zu können.