PHP und XML - Comelio Medien
Transcription
PHP und XML - Comelio Medien
XPath-Abfragen 8 XPath-Abfragen 419 XPath-Abfragen 420 XPath-Abfragen 8 XPath-Abfragen Wie an verschiedenen Stellen angekündigt, soll die XPath-Syntax an dieser Stelle im Zusammenhang kurz in der Version 1.0, die in den PHP-Werkzeugen genutzt werden kann, vorgestellt werden. Sie liefern bei der Nutzung des DOM, XSLT oder SimpleXML Ergebnismengen wie bei einer Datenbankabfrage zurück. Teilweise ersetzen oder vereinfachen Sie DOM-Methoden oder DOM-Eigenschaften bzw. PHPAlgorithmen wie Fallunterscheidungen. Teilweise bieten die PHP-Werkzeuge Vereinfachungen an, sodass ein XPath-Ausdruck, der besonders häufig bspw. in XSLT auftreten würde, in PHP über eine Funktion, Methode oder Eigenschaft (je nach Technologie) möglich ist. Im Regelfall wird man bei umfangreichem XPath-Einsatz vermutlich eher auf komplett auf XSLT ausweichen und ansonsten XPath wie SQL für XML-Daten einsetzen. 8.1 Abfrage mit XPath Wie schon in verschiedenen PHP-Beispielen und natürlich auch in XSLT gezeigt, benötigt man für die Navigation innerhalb eines XML-Dokuments eine möglichst kurze Ausdruckssprache. Dies ist sowohl in XSLT wie auch im DOM oder SimpleXML die Navigations- und Abfragesprache XPath. Im Rahmen anderer XML-Technologien wie XML Schema oder XForms kommt sie darüber hinaus ebenfalls zum Einsatz, sodass eine Beschäftigung mit ihrer Syntax eine sehr sinnvolle Investition ist. Darüber hinaus lassen sich XPath-Ausdrücke auch für die Abfrage von XML-Strukturen in Großdatenbanken wie Oracle oder dem MS SQL Server nutzen. Sie besitzt keine XML-Form, damit sie möglichst kurze Ausdrücke hervorbringt, welche allerdings bei steigenden Anforderungen auch sehr umfangreich werden können. Es lässt sich oft beobachten, dass die tatsächlich komplizierten Syntaxstellen in Transformationsalgorithmen in XSLT insbesondere die XPath-Ausdrücke darstellen. Hier entscheidet sich, wie der Programmierer die Daten abruft, vergleicht und mit eingebauten 421 XPath-Abfragen Funktionen bearbeitet. Die Version 2.0 bietet sehr viele Erweiterungen, welche die Bedeutung dieser Technik noch deutlich erhöht haben. Die offiziellen Dokumente findet man unter XML Path Language (XPath), Version 1.0, W3C Recommendation 16 November 1999 (http://www.w3.org/TR/xpath) und XML Path Language (XPath) 2.0, W3C Candidate Recommendation 3, November 2005(http://www.w3.org/TR/xpath20/). Die Version 1.0 ist für PHP weiterhin gültig. 8.1.1 Knoten und Achsen Ein XPath-Ausdruck findet sieben verschiedene Knotenarten, für die es jeweils eine eigene Syntax gibt. Die Typologie ist in der nachfolgenden Liste dargestellt. Wichtig ist, dass zwischen dem Knotensystem des Document Object Model und demjenigen von XPath verschiedene Unterschiede bestehen. Einer der wesentlichste bezieht sich auf den Textknoten. Für XPath ist ein Textknoten der Zeichenketteninhalt eines Elements, während Attribute einen Wert haben. Das DOM dagegen betrachtet beide als Textknoten. Wurzelknoten (engl. root nodes) Elementknoten (engl. element nodes) Textknoten (engl. text nodes) Attributknoten (engl. attribute nodes) Namensraumknoten (engl. namespace nodes) Prozessoranweisungen (engl. processing instruction nodes) Kommentare (engl. comment nodes) Als Beispiel verwendet dieses Kapitel die bereits bekannte Datei erfolguebersicht.xml, wobei hier die Variante mit den zwei Attributen Stadt und Monat zum Einsatz kommt. 422 XPath-Abfragen Prozessoranweisung Kommentar Wurzel <?xml version="1.0" encoding="ISO-8859-1"?> Namensraum <?xml-stylesheet type="text/xsl" href="erfolguebersicht.xslt"?> <!-- Erfolgszahlen 2003 --> <Erfolguebersicht xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="erfolguebersicht.xsd"> <Erfolg Stadt="Düsseldorf" Monat="7"> <Gesamt>75</Gesamt> Attribut <Neukunden>4</Neukunden> </Erfolg> Element Text Abbildung 8.1: Knoten-Typen Knoten wie Elemente, Attribute und Namensräume werden über so genannte Achsen angesteuert, die in Dokumentrichtung (westliche Leserichtung, von oben nach unten) oder entgegen gesetzt zur Dokumentrichtung (von unten nach oben) Knoten finden. Sie ähneln einem Familienstammbaum und decken den gesamten Baum ab, der von einem XML-Dokument aufgespannt wird. Vorwärts Rückwärts child (Kind) parent (Eltern) descendant (Nachfolger) ancestor (Vorfahren) attribute (Attribut) self (Ich) preceding-sibling Geschwister) descendant-or-self (Nachfolger und ich) preceding (Vorhergehende) following-sibling (Folgende Geschwister) ancestor-or-self (Vorfahren und ich) (Vorhergehende following (Folgende) namespace (Namensraum) Achsentypen 423 XPath-Abfragen Die nachfolgenden Abbildungen sollen zusammen mit den Beispielen die Verwendung der verschiedenen Achsen demonstrieren. Die Knoten werden noch ohne Prädikate, d.h. ohne Bedingungen ausgewählt. Als Beispieldokument verwenden sie das bereits an verschiedener Stelle beschriebene tabellenähnliche Dokument erfolguebersicht.xml. 1 Abbildung 8.2: Achsen 1 424 parent (Eltern-Achse) XPath-Abfragen child (Kind-Achse) 1 2 3 descendant (Nachfolger-Achse) 1 2 4 5 3 Abbildung 8.3: Achsen 2 Beispiele, ausgehend vom Wurzelknoten: child::* findet das Wurzelelement Erfolguebersicht in Kurzschreibweise (alle Kinder vom Wurzelknoten). child::Erfolguebersicht findet das Wurzelelement Erfolguebersicht in Langschreibweise. 425 XPath-Abfragen child::Erfolguebersicht/child::Erfolg/child::Neukunden findet alle Neukunden-Elemente, die Kinder von Erfolg, die Kinder von Erfolguebersicht sind. Kurzschreibweise: /Erfolguebersicht/Erfolg/Neukunden. /Erfolguebersicht/Erfolg/descendant::* findet von jedem Erfolg Neukunden und Gesamt und deren Kinder und Kindeskinder (allerdings nicht vorhanden). preceding-sibling (Vorherige-Geschwister-Achse) 2 1 Abbildung 8.4: Achsen 3 Beispiel ausgehend von einem Erfolg: parent::* findet in Kurzschreibweise das Eltern-Element Erfolguebersicht, Langschreibweise: parent::Erfolguebersicht. Beispiel ausgehend von einem Gesamt-Element: parent::Erfolg/Neukunden findet in Kurzschreibweise die zu einem GesamtElement gehörenden Neukunden, Langschreibweise: parent::Erfolg/child:: Neukunden, Alternative: Auswahl über die following-sibling-Achse. 426 XPath-Abfragen 2 ancestor (Eltern-Achse) 1 following-sibling (Folgende-Geschwister-Achse) 1 2 Abbildung 8.5: Achsen 4 Beispiele ausgehend von einem Erfolg-Element: following-sibling::Erfolg Dokumentrichtung. findet weitere Erfolg-Elemente in following-sibling::Erfolg/child::Gesamt findet in Langschreibweise die Gesamt-Elemente, die zu den in Dokumentrichtung folgenden Erfolg-Elemenen gehören. 427 XPath-Abfragen preceding (Vorherigen-Achse) 2 3 1 following (Nachfolger-Achse) 1 2 3 4 5 Abbildung 8.6: Achsen 5 Beispiele ausgehend von einem Gesamt-Element: ancestor::* findet entgegengesetzt zur Dokumentrichtung, Erfolg, Erfolguebersicht. ancestor::Erfolg findet entgegengesetzt zur Dokumentrichtung die zugehörige Erfolg. 428 XPath-Abfragen ancestor::Erfolg/preceding-sibling::* findet Dokumentrichtung die vorangegangenen Erfolg-Elemente. entgegengesetzt zur descendant-or-self (Nachfolger-und-ich-Achse) 1 2 3 5 6 4 3 2 1 ancestor-or-self (Vorfahren-und-ich-Achse) Abbildung 8.7: Achsen 6 429 XPath-Abfragen self (Ich-Achse) 1 Abbildung 8.8: Achsen 7 Beispiele ausgehend von einem Erfolg-Element: following::* findet nachfolgende Erfolg-Elemente und deren Kinder und Kindeskinder. following::Erfolg findet nachfolgenden Erfolg-Elemente.. 8.1.2 Prädikate Die einfache Auswahl von sämtlichen Knoten einer Achse ist nicht so interessant, wie die Verwendung von Prädikaten, welche quasi die WHERE-Klausel eines XPathAusdrucks angeben. Ein solcher setzt sich aus drei Komponenten zusammen, die als Einheit als Lokalisierungsschritt bezeichnet werden: 1. Eine Achse, die die Beziehung zwischen dem gerade ausgewählten und verarbeiteten Kontextknoten und den Knoten ausdrückt, die über den Lokalisierungspfad auszuwählen sind. 430 XPath-Abfragen 5. Einen Knotentest, der den Knotentyp und den expandierten Namen der Knoten angibt, die durch den Lokalisierungspfad auszuwählen sind. 6. Kein oder mehrere Prädikate, die beliebige Ausdrücke verwenden, um genauere Eigenschaften anzugeben, die für die Knoten zutreffen sollen, die durch den Lokalisierungspfad auszuwählen sind. Wie in SQL sind auch die verschiedenen Operatoren für Vergleiche, Berechnungen und Logik verfügbar, wobei in XSLT wie in HTML die entsprechenden Entitäten zum Einsatz kommen müssen. Als Ergebnis erhält man SQL-ähnliche Abfragemöglichkeiten, die keine XML-Dokumente zurückliefern, sondern Ergebnismengen, die bspw. mit XSLT verarbeitet werden können oder die in PHP werden können. Vergleich Bedeutung Kalkül Bedeutung Logik Bedeutung = Gleichheit + Addition and Und != Ungleichheit - Subtraktion or Oder <, < kleiner * Multiplikation not Nicht >; > größer div Division <=, <= kleiner gleich mod >=, >= größer gleich Modulo Operatoren in XPath-Prädikaten Die Beispiele bieten neben der Verwendung von Operatoren auch die Möglichkeit, sich näher mit der Syntax an sich zu beschäftigen. So ist es möglich, innerhalb der Prädikate wiederum über die einzelnen Achsen andere Knoten auszuwählen. Wichtig ist zudem, sich darüber im Klaren zu sein, welche Knotenmenge tatsächlich benötigt wird. Soll der Zugriff auf die Erfolg oder den Neukunden gegeben sein? Beides kann im aktuellen Fall sinnvoll sein, erfordert aber einen anderen Ausdruck. Die nachfolgenden Beispiele gehen vom Wurzelknoten aus: 431 XPath-Abfragen Welche Erfolg-Elemente haben mehr als zwei Neukunden? //Erfolg[Neukunden > 2] Alle Erfolge aus Düsseldorf? //Erfolg[@Stadt = 'Düsseldorf'] Alle Erfolge aus Düsseldorf vom Frühling? //Erfolg[@Stadt = //Erfolg[@Stadt = 'Düsseldorf' and @Monat >2 and @Monat <= 5] Alle Neukunden von Erfolgen aus Düsseldof? 'Düsseldorf']/Neukunden Alle Neukunden größer fünf von Erfolgen aus Düsseldof? //Erfolg[@Stadt = 'Düsseldorf']/Neukunden[. > 5] oder //Erfolg[@Stadt = 'Düsseldorf' and Neukunden > 5]/Neukunden, wobei der Punkt den Textinhalt repräsentiert. Die nachfolgenden Beispiele gehen von einem Erfolg-Element aus: Alle Monate der nachfolgenden Erfolg-Elemente aus Düsseldorf? following- sibling::Erfolg[@Stadt = 'Düsseldorf']/@Monat Alle nachfolgenden Erfolg-Elemente aus Düsseldorf nach Oktober? following- sibling::Erfolg[@Stadt = 'Düsseldorf'][@Monat > 10] oder followingsibling::Erfolg[@Stadt = 'Düsseldorf' and @Monat > 10]. 8.1.3 Funktionen Schließlich gibt es noch eine kleine Bibliothek an Funktionen, mit denen bspw. basale Rechen- und Zeichenkettenmanipulationen vorgenommen werden können. In der Version 2.0 ist diese Bibliothek extrem erweitert worden, kann für PHP allerdings noch nicht genutzt werden. Die Knotenmengen-Funktionen ermitteln allgemeine Informationen über Knoten oder erlauben den Zugriff auf Knoten über diese Informationen. position() ermittelt die aktuelle Position. last() liefert den letzten Knoten einer Knotenmenge. 432 XPath-Abfragen count(Knotenmenge) zählt die Knoten in einer Knotenmenge. name(Knotenmenge) ermöglicht die direkte Auswahl von Knoten anhand ihres Namens mit Namensraumpräfix. local-name(Knotenmenge) ermöglicht die direkte Auswahl von Knoten anhand ihres Namens ohne Namensraumpräfix. namespace-uri(Knotenmenge) ermittelt den URI-Wert für einen Namensraum. id(Objekt) ermittelt die Knotenmengen mit der angegebenen ID Die Zeichenketten-Funktionen ermöglichen ebenfalls basale Manipulation von Zeichenketten für die Ausgabe in XSLT oder die Suche in XPath-Prädikaten. starts-with(zk1, Zeichenkette 2 beginnt. zk2) liefert true, wenn Zeichenkette 1 mit der contains(zk1, zk2) liefert liefert true, wenn Zeichenkette 1 in Zeichenkette 2 auftritt. substring-before(zk1, zk2) ermittelt die Zeichen einer Zeichenkette 1 vor der Position, die in Zeichenkette 2 angegeben wurde: substring-before("12345", "3") liefert 12. substring-after(zk1, zk2) ermittelt die Zeichen einer Zeichenkette 1 nach der Position, die in Zeichenkette 2 angegeben wurde: substring-after("12345", "3") also die Zahl 45. substring(zk1, zk2, länge) ermittelt die Zeichen einer Zeichenkette 1 vor der Position, die in Zeichenkette 2 angegeben wurde, für die angegebene Länge: substring("123456789", "3", "2") liefert 34. string-length(zk) ermittelt die Länge einer Zeichenkette. string(Objekt) transformiert ein Objekt in eine Zeichenkette. concat(zk1, zk2, ...) verknüpft die eingehenden Zeichenketten. 433 XPath-Abfragen Die logischen Funktionen liefern Wahrheitswerte oder kehren diese um. true() liefert den Wert wahr. false() liefert den Wert falsch. not(Objekt) liefert den Wert wahr, wenn das Objekt falsch ist. boolean(Objekt) transformiert ein Objekt in einen logischen Wert, wobei nicht leere Knotenmengen und Zeichenketten sowie Zahlen größer 0 den Wert wahr ergeben. lang(zk) liefert den Wert wahr, wenn der xml:lang-Wert (Standardattribut für ISO-Sprachkürzel wie de, en, fr usw.) mit der in der Zeichenkette angegebenen Sprache übereinstimmt. Die numerischen Funktionen führen basale Rechenoperationen durch, wobei allerdings insbesondere das Runden nur mit Version 2.0 wie in anderen Sprachen mit Dezimalstellenangabe funktioniert. number(Objekt) transformiert ein Objekt in eine Zahl. sum(Knotenmenge) berechnet die Summe der zu Zahlen transformierten Werte der Knotenmenge. floor(Zahl) liefert die vorher liegende Ganzzahl. ceiling(Zahl) liefert die nächstgrößere Ganzzahl. round(Zahl) liefert den gerundeten Wert zur nächsten Ganzzahl. Die nächsten Beispiele zeigen die Mächtigkeit der kleinen Funktionsbibliothek: Wieviele Erfolge sind vorhanden? count(//Erfolg) Der fünfte Erfolg? //Erfolg[5] ode //Erfolg[position() = 5] Der letzte Erfolg? //Erfolg[position()=last()] Wieviele Neukunden pro Monat? sum(//Neukunden) div 12 434 XPath-Abfragen Wieviel Prozent Neukunden aus Düsseldorf? sum(//Erfolg[@Stadt = div sum(//Neukunden) * 100 oder mit Ganzzahlrundung und Prozentzeichen concat(round(sum(//Erfolg[@Stadt = 'Düsseldorf']/Neukunden) 'Düsseldorf']/Neukunden) div sum(//Neukunden) * 100), '%') Alle Erfolge mit einem Stadtnamen, der mit groß geschriebenem B beginnt? //Erfolg[starts-with(@Stadt, 'B')] Alle Erfolge mit einem Stadtnamen, der ein klein geschriebenes B enthält? //Erfolg[contains(@Stadt, 'b')] Alle Erfolge mit einem Stadtnamen, der mehr als fünf Buchstaben hat? //Erfolg[string-length(@Stadt)>5] 435