XML, XPath, PHP: DOMXPath::query() funktioniert nicht?

Möchte man XML-Daten dynamisch einlesen und den Code möglichst generisch gestalten, ist DOMXPath::query() sehr hilfreich: Die Pfade können in einer separaten Datenquelle als Zeichenketten hinterlegt werden.

Liest man Tutorials auf php.net und in anderen Blogs, bleibt oft ein für die Praxis wichtiges Detail unerwähnt. Es folgt ein kurzes Beispiel einer XML-Antwort:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<company xmlns:xlink="http://www.w3.org/1999/xlink"
         xmlns="http://api.xonu.de" id="123">
    <name>xonu</name>
</company>

Das Auslesen der id über XPath funktioniert problemlos:

$xpathStr = '@id';

// $xmlStr <-- XML-Antwort
$domDoc = new DOMDocument();
$domDoc->loadXML($xmlStr);
$domXpath = new DOMXPath($domDoc);

$domNode = $domXpath->query($xpathStr);

if($domNode->length) {
    print $domNode->item(0)->nodeValue;
} else {
    print 'Nichts gefunden.';
}

Die Ausgabe dieses Skripts ist der Wert des id-Attributes des Wurzelknotens: 123. Nun wollen wir den Wert des Unterknotens name auslesen:

$xpathStr = 'name';

Doch das funktioniert nicht, die Ausgabe ist „Nichts gefunden.“. Verschiedene äquivalente Formulierungen des Pfades für das id-Attribut funktionieren, aber nicht für den Unterknoten name:

$xpathStr = '@id'; // Ausgabe: 123
$xpathStr = '//@id'; // Ausgabe: 123
$xpathStr = '/*/@id'; // Ausgabe: 123
$xpathStr = 'name'; // Ausgabe: Nichts gefunden.
$xpathStr = '//name'; // Ausgabe: Nichts gefunden.
$xpathStr = '/*/name'; // Ausgabe: Nichts gefunden.

Durch Entfernen des xmlns-Attributs kommt man dem Problem auf die Spur: Dieses Attribut fehlt oft in den Tutorial-Beispielen, ist aber in der Praxis meistens vorhanden! Nach dem Entfernen von xmlns=“http://api.xonu.de“ aus der XML-Antwort, funktionieren alle oben aufgelisteten X-Pfade.

Der in xmlns angegebener XML-Namespace, also der Namensraum, muss explizit registriert werden:

$xmlNs = $domDoc->documentElement->lookupnamespaceURI(null);
$domXpath->registerNamespace('x', $xmlNs);

Jetzt können wir den Unterknoten name über die folgenden X-Pfade abfragen:

$xpathStr = 'x:name';
$xpathStr = '//x:name';
$xpathStr = '/*/x:name';

Es könnten allerdings bereits hunderte X-Pfade definiert worden sein (z. B. im Profil eines XML-Adapters), die nun alle überarbeitet werden müssten, um sich auf einen bestimmten Namensraum zu beziehen. Um dies zu vermeiden, kann das xmlns-Attribut programmatisch gelöscht werden:

$xmlNs = $domDoc->documentElement->lookupnamespaceURI(null);
// $domXpath->registerNamespace('x', $xmlNs);
$domDoc->documentElement->removeAttributeNS($xmlNs, '');

Nun würden alle X-Pfade ohne eines expliziten Bezugs auf einen Namensraum funktionieren.

Beispielcode zum Ausprobieren herunterladen (domxpath-example.php)

Ein Gedanke zu „XML, XPath, PHP: DOMXPath::query() funktioniert nicht?“

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.