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
<, &lt;
kleiner
*
Multiplikation
not
Nicht
>; &gt;
größer
div
Division
<=, &lt;=
kleiner gleich mod
>=, &gt;=
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