XPath ist eine ziemlich geniale Sache. Es erlaubt die Formulierung von Ausdrücken, die in einer XML-Datei nach gewissen Dingen suchen. Dabei können einzelne, bestimmte oder alle Textinhalte eines Tags, Attributswerte und die Anzahl gesucht und gefunden werden, und das alles jeweils relativ oder absolut zum Root des XML-Dokuments. Die Möglichkeiten sind dabei ziemlich flexibel und es drängt sich ein Vergleich zu SQL auf.
Dies soll kein XPath-Tutorial werden, das machen Andere, an anderen Stellen viel besser.
Ich wollte etwas ähnliches bauen wie Futurelab (Link leider tot 🙁 ) in Java/JSP realisiert hat, allerdings in PHP. Ausserdem wollte ich, dass es auch mit dem count() Ausdruck und seinen Kollegas umgehen kann.
Programmaufbau
Was XML angeht, kann sich PHP echt nicht beklagen; Mindestens 4 Ansatzpunkte bieten sich dem interessierten Coder. Als einfaches Gemüt mit Freude an KISS (und KISSES 🙂 ), wollte ich natürlich zuerst SimpleXML bemühen. Leider liefert die dazugehörige XPath-Funktion „nur“ die Ergebnisse als String. Dies verunmöglicht ein Highlightning im original-XML. Einen Gang hochgeschaltet und den grösseren Bruder bemüht: Die DOM Erweiterung.
Der Grundsätzliche Ablauf des Laborprogramms ist der Folgende:
- Flatfile XML wird in einen DOM-Tree umgewandelt: Ein Aggregat aus Objekten, Attributen, Arrays und was man sonst noch so in einem handelsüblichen Speicher findet.
- Er wird versucht, aus den Eingaben des Anwenders Sinn zu gewinnen → die entsprechenden Teile im DOM-Tree mittels XPath zu identifizieren.
- Diese Teile werden irgendwie markiert (schwierigster Teil).
- Der Dom-Tree wird wieder in ein XML geplättet.
- Die Marker werden durch Spans mit Stylesheetinfos ersetzt, um zu zeigen was gefunden wurde.
Fälle und Fallen
Wenn der Anwender etwas sucht, werden vier Fälle unterschieden. Diese Fallunterscheidung wird an Hand des Resultats der XPath Anfrage gemacht. Anzahl der Resultate und Typ (mit Hilfe des Reflection APIs herausgepfriemelt, wohoo(!!!)) bestimmen die Aktion.
Fall 1: Es gibt Nodes im Resultat und die sind vom Typ DOMElement
Juhuu, wir haben simple und einfache Dinge gefunden. Nun müssen wir sie noch schnell markieren. Meine Idee war, dies mit Kommentaren zu machen, die ich jeweils vor vor und nach dem gefundenen Element einsetze:
$start=$xo->createComment('MATCH-START');
$end=$xo->createComment('MATCH-END');
$result->parentNode->insertBefore($start,$result->previousSibling);
$result->parentNode->insertBefore($end,$result->nextSibling);
Bug-Potential 1: Es dürfen keine identischen Kommentare im original-XML enthalten sein.
Fall 2: Es gibt Nodes im Resultat und die sind vom Typ DOMAttr
Etwas komplexer: Es ist nach Attributen gesucht worden. Hier wollte ich zuerst Regexpe zum Highlightnen speichern, aber das funktioniert nicht, da einzelne Attribute bestimmter Knoten ausgewählt werden können (//cd[2]/@currency). Naja, dann wurschteln wir hier Marker <!--MATCH-START--> und <!--MATCH-END--> in den Attributsinhalt. Leider werden die Klammern schon mal mit ihren HTML-Entities ersetzt, was dann zum Schluss eine doppelte Codierung ergibt.
Fall 3: Es gibt keine Nodes im Resultat, aber evaluate bringt ein Ergebnis
Es wird eine Funktion wie etwa count() verwendet. Nundenn: Evaluieren, ausgeben, danke.
Fall 4: Es gibt keine Nodes im Resultat und evaluate bringt kein Ergebnis
Das ist eine ID-10-T Eingabe. Fehlermeldung und tschüss.
Das Resultat
Das Resultat kann angesehen und als Gesamtheit mit Quellcode heruntergeladen werden. Kommentare und Bugreports würden mich freuen. Happy coding!!!!
