Achsen effektiv benutzen

(Auszug aus "XSLT Kochbuch" von Sal Mangano)

Problem

Sie müssen Knoten in einem XML-Baum so auswählen, dass die komplexen Beziehungen innerhalb der hierarchischen Struktur beachtet werden.

Lösung

Jede der folgenden Lösungen ist um verwandte Mengen von Achsen herum organisiert. Für jede Gruppe wird ein Beispiel-XML-Dokument präsentiert, bei dem der Kontextknoten hervorgehoben ist. Die Auswertung des Pfades wird erläutert, außerdem werden die Knoten fett gekennzeichnet, die unter Beachtung des hervorgehobenen Kontextes ausgewählt werden. In einigen Fällen berücksichtigt die Lösung andere Knoten als Kontext, um die Feinheiten des speziellen Pfadausdrucks zu verdeutlichen.

Child- und Descendant-Achsen

Die Child-Achse ist die Standardachse in XPath. Das bedeutet, dass man die Achsenspezifikation child:: nicht benutzen muss. Falls Sie jedoch ein wenig pedantisch sind, können Sie es natürlich tun. Mit den descendant::- und descendant-or-self::-Achsen kann man tiefer in den XML-Baum gelangen. Die Erstgenannte schließt den Kontextknoten aus, die Zweite bezieht ihn mit ein.

<Test id="descendants">
  <parent>
    <X id="1"/>
    <X id="2"/>
    <Y id="3">
      <X id="3-1"/>
      <Y id="3-2"/>
      <X id="3-3"/>
    </Y>
    <X id="4"/>
    <Y id="5"/>
    <Z id="6"/>
    <X id="7"/>
    <X id="8"/>
    <Y id="9"/>
  </parent>
</Test>

(: Auswählen aller Kindelemente namens X :)

X (: identisch mit child::X :)

Ergebnis: <X id="1"/> <X id="2"/> <X id="4"/> <X id="7"/> <X id="8"/>

(: Auswählen des ersten X-Kindelements :)

X[1]

Ergebnis: <X id="1"/>

(: Auswählen des letzten X-Kindelements :)

X[last( )]

Ergebnis: <X id="8"/>

(: Auswählen des ersten Elements, vorausgesetzt, es ist ein X. Ansonsten leer :)

*[1][self::X]

Ergebnis: <X id="1"/>

(: Auswählen des letzten Kindes, vorausgesetzt, es ist ein X. Ansonsten leer :)

*[last( )][self::X]

Ergebnis: ( )

(: Auswählen des letzten Kindes, vorausgesetzt, es ist ein Y. Ansonsten leer :)

*[last( )][self::Y]

Ergebnis:<Y id="9"/>

(: Auswählen aller Nachfahren namens X :)

descendant::X

Ergebnis: <X id="1"/> <X id="2"/> <X id="3-1"/> <X id="3-3"/> <X id="4"/> <X id="7"/> <X id="8"/>

(: Auswählen des Kontextknotens, falls dieser ein X ist, und aller Nachfahren namens X :)

descendant-or-self::X

Ergebnis: <X id="1"/> <X id="2"/> <X id="3-1"/> <X id="3-3"/> <X id="4"/> <X id="7"/> <X id="8"/>

(: Auswählen des Kontextknotens und aller Nachfahren :)

descendant-or-self::*

Ergebnis: <parent> <X id="1"/> <X id="2"/> <Y id="3"> <X id="3-1"/> <Y id="3-2"/> <X id="3-3"/> </Y> <X id="4"/> <Y id="5"/> <Z id="6"/> <X id="7"/> <X id="8"/> <Y id="9"/> </parent> <X id="1"/> <X id="2"/> <Y id="3"> <X id="3-1"/> <Y id="3-2"/> <X id="3-3"/> </Y> <X id="3-1"/> <Y id="3-2"/> <X id="3-3"/> <X id="4"/> <Y id="5"/> <Z id="6"/> <X id="7"/> <X id="8"/> <Y id="9"/>

Sibling-Achsen

Zu den Sibling-Achsen gehören preceding-sibling:: und following-sibling::. Die Preceding-sibling-Achse besteht aus den Geschwistern, die dem Kontextknoten vorangehen, die Following-sibling-Achse besteht aus den Geschwistern (siblings), die ihm folgen. Geschwister sind natürlich Kindknoten, die dieselben Eltern haben. Die meisten der unten gezeigten Beispiele verwenden preceding-sibling::. Sie sollten aber ohne größere Schwierigkeiten in der Lage sein, die Ergebnisse für following-sibling:: zu ermitteln.

Denken Sie daran: Wenn Sie einen Positionspfadausdruck der Form preceding-sibling::*[1] verwenden, beziehen Sie sich auf das unmittelbar vorhergehende Geschwister vom Kontextknoten aus gesehen und nicht auf das erste Geschwister in der Dokumentenreihenfolge. Manche Leute kommen immer durcheinander, weil die resultierende Sequenz in der Dokumentenreihenfolge vorliegt, unabhängig davon, ob sie preceding-sibling:: oder following-sibling:: benutzen. Obwohl es nicht direkt ein Achsenausdruck ist, bildet ../X eine Möglichkeit zu sagen: »Wähle sowohl die vorhergehenden als auch die nachfolgenden Geschwister namens X sowie den Kontextknoten aus«, sollte dieser ein X sein. Formeller ausgedrückt ist es eine Abkürzung für parent::node( )/X. Beachten Sie, dass (preceding-sibling::*)[1] und (following-sibling::*)[1] das erste vorhergehende/folgende Geschwister in der Dokumentenreihenfolge auswählen.

<!-- Beispieldokument mit hervorgehobenem Kontextknoten -->
<Test id="preceding-siblings">
  <A id="1"/>
  <A id="2"/>
  <B id="3"/>
  <A id="4"/>
  <B id="5"/>
  <C id="6"/>
  <A id="7"/>
  <A id="8"/>
  <B id="9"/>
</Test>

(: Auswählen aller A-Geschwisterknoten, die dem Kontextknoten vorangehen. :)

preceding-sibling::A

Ergebnis: <A id="1"/> <A id="2"/> <A id="4"/>

(: Auswählen aller A-Geschwisterknoten, die dem Kontextknoten folgen. :)

following-sibling::A

Ergebnis: <A id="8"/>

(: Auswählen aller Geschwisterelemente, die dem Kontextknoten vorangehen. :)

preceding-sibling::*

Ergebnis: <A id="1"/> <A id="2"/> <B id="3"/> <A id="4"/> <B id="5"/> <C id="6"/>

(: Auswählen des ersten vorhergehenden Geschwisterelements namens A in umgekehrter Dokumentenreihenfolge. :)

preceding-sibling::A[1]

Ergebnis: <A id="4"/>

(: Das erste vorhergehende Element in umgekehrter Dokumentenreihenfolge, vorausgesetzt, es ist ein A. :)

preceding-sibling::*[1][self::A]

Ergebnis:( )

(: Wenn der Kontext <A id="8"/> wäre, dann wäre das Ergebnis <A id="7"/> :)

(: Alle vorhergehenden Geschwisterelemente, die nicht A-Elemente sind: )

preceding-sibling::*[not(self::A)]

Ergebnis: <B id="3"/> <B id="5"/> <C id="6"/>

(: Benutzen Sie für die folgenden Rezepte dieses Dokument. :)

<Test id="preceding-siblings">
  <A id="1">
    <A/>
  </A>
  <A id="2"/>
  <B id="3">
    <A/>
  </B>
  <A id="4"/>
  <B id="5"/>
  <C id="6"/>
  <A id="7"/>
  <A id="8"/>
  <B id="9"/>
</Test>

(: Das Element, das dem Kontext direkt vorausgeht, vorausgesetzt, es hat ein Kindelement A :)

preceding-sibling::*[1][A]

Ergebnis: ( )

(: Das erste Element, das dem Kontext vorausgeht, das ein Kind A hat :)

preceding-sibling::*[A][1]

Ergebnis: <B id="3"> ...

(: XPath 2.0 erlaubt eine größere Flexibilität bei der Auswahl von Elementen unter Beachtung von Namensräumen. Für diese Rezepte gilt das folgende XML-Dokument. :)

<Test xmlns:NS="http://www.ora.com/xstlcbk/1" xmlns:NS2="http://www.ora.com/xstlcbk/2">
  <NS:A id="1"/>
  <NS2:A id="2"/>
  <NS:B id="3"/>
  <NS2:B id="3"/>
</Test>

(: Auswählen der vorhergehenden Geschwisterelemente des Kontexts, dessen Namensraum mit dem Präfix NS verknüpft ist :)

preceding-sibling::NS:*

Ergebnis: <NS:A id="1"/>

(: Auswählen der vorhergehenden Geschwisterelemente des Kontexts, dessen lokaler Name A ist :)

preceding-sibling::*:A

Ergebnis: <NS:A id="1"/>, <NS2:A id="2"/>

Parent- und Ancestor-Achsen

Die Parent-Achse (parent::) bezieht sich auf die Eltern des Kontextknotens. Der Ausdruck parent::X darf nicht mit ../X verwechselt werden. Der erstgenannte Ausdruck erzeugt eine Sequenz aus genau einem Element, vorausgesetzt, der Elternknoten des Kontexts ist X oder leer. Der zweite Ausdruck ist eine Kurzform von parent::node( )/X. Damit werden alle Geschwister des Kontextknotens namens X ausgewählt, einschließlich des Kontextknotens selbst, sollte dieser X sein.

Man kann sich auf höhere Stufen des XML-Baums (Eltern, Großeltern, Urgroßeltern usw.) begeben, indem man entweder ancestor:: oder ancestor-or-self:: benutzt. Bei der ersten Form wird der Kontext nicht einbezogen, bei der zweiten Form wird er eingeschlossen.

(: Auswählen des Elternknotens des Kontextknotens, vorausgesetzt, es ist ein X-Element. Ansonsten leer. :)

parent::X

(: Auswählen des Elternelements des Kontextknotens. Kann nur leer sein, wenn der Kontext das oberste Element ist. :)

parent::*

(: Auswählen des Elternknotens, wenn dieser in dem Namensraum liegt, der mit dem Präfix NS verknüpft ist. Das Präfix muss definiert sein, ansonsten tritt ein Fehler auf. :)

parent::NS:*

(: Auswählen des Elternknotens ungeachtet seines Namensraums, vorausgesetzt, der lokale Name ist X. :)

parent::*:X

(: Auswählen aller Vorfahrenelemente (einschließlich des Elternelements) namens X. :)

ancestor::X

(: Auswählen des Kontexts, vorausgesetzt, es ist ein X, und aller Vorfahrenelemente namens X. :)

ancestor-or-self::X

Preceding- und Following-Achsen

Die Preceding- und Following-Achsen bieten die Möglichkeit, eine große Anzahl von Knoten auszuwählen, da sie alle Knoten beachten, die in der Dokumentenreihenfolge vor (nach) dem Kontextknoten kommen, allerdings ohne Vorfahrenknoten (Ancestorknoten). Die Following-Achse schließt Nachfahrenknoten aus, die Preceding-Achse schließt Vorfahren aus. Vergessen Sie außerdem nicht: Beide Achsen schließen Namensraumknoten und Attribute aus.

(: Alle vorhergehenden Elementknoten namens X. :)

preceding::X

(: Der nächste vorhergehende Elementknoten namens X. :)

preceding::X[1]

(: Der am weitesten weg gelegene folgende Elementknoten namens X. :)

following::X[last( )]

Diskussion

XPath verwendet das Konzept einer Achse, um den Dokumentbaum in Teilmengen zu unterteilen, die relativ zu einem bestimmten Knoten, dem sogenannten Kontextknoten, liegen. Im Allgemeinen überschneiden sich diese Teilmengen, allerdings untergliedern die Ancestor-, Descendant-, Following-, Preceding- und Self-Achsen ein Dokument (Attribut- und Namensraumknoten werden ignoriert): Sie überschneiden sich nicht, und zusammen enthalten sie alle Knoten im Dokument. Der Kontextknoten wird durch die Sprache XPath etabliert. In XSLT wird der Kontext folgendermaßen gesetzt:

Die effektive Handhabung der Arten der Pfadausdrücke, die in der Lösung gezeigt wurden, ist der Schlüssel zur Durchführung sowohl einfacher als auch komplexer Transformationen. Erfahrungen mit traditionellen Programmiersprachen führen beim Einsatz von XPath manchmal zu Verwirrung und Fehlern. Beispielsweise habe ich mich schon oft dabei erwischt, Folgendes zu schreiben: <xsl:if test="preceding-sibling::X[1]"> </xsl:if>, wenn ich eigentlich <xsl:if test="preceding-sibling::*[1][self::X]"> </xsl:if> schreiben wollte. Das liegt möglicherweise daran, dass der zweite Ausdruck die weniger intuitive Methode ist, »teste, ob das unmittelbar vorhergehende Geschwisterelement ein X ist« zu sagen.

Es ist natürlich unmöglich, jede nützliche Permutation von Pfadausdrücken zu zeigen, die Achsen benutzt. Wenn Sie jedoch die Grundbausteine verstehen, die ich gerade gezeigt habe, sind Sie auf dem besten Wege, die Bedeutung von Konstrukten wie preceding-sibling::X[1]/descendant::Z[A/B] oder schlimmeren zu dekodieren.

  

zum Seitenanfang

<< zurück vor >>

 

 

 

Tipp der data2type-Redaktion:
Zum Thema XSLT bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an:

Copyright © 2006 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 "XSLT Kochbuch" 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