Пути DOMXPath отображаются в виде звездочек
У меня есть строка XML, фрагмент ниже:
<?xml version="1.0"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<SendPurchases xmlns="urn:services.insurance">
<Partner>
<UserID>MyCompany</UserID>
<Password>ABC123</Password>
</Partner>
<PurchasesRequest>
<Total>100</Total>
</PurchasesRequest>
</SendPurchases>
</soap:Body>
</soap:Envelope>
Я конвертирую XML в DOMDocument, чтобы сделать манипуляции "проще":
$doc = new DOMDocument();
$doc->loadXML($xml);
$xpath = new DOMXPath($doc);
Затем я хотел бы манипулировать определенными значениями, предпочтительно используя их пути:
$elements = $xpath->query('/soap:Envelope/soap:Body/SendPurchases/Partner/UserID');
Однако приведенный выше запрос не возвращает результатов.
Фактически, когда я перебираю все элементы внутри документа:
foreach ($doc->getElementsByTagName('*') as $node) {
echo $node->getNodePath() . "\n";
}
Это возвращает что-то вроде этого:
/soap:Envelope/soap:Body
/soap:Envelope/soap:Body/*
/soap:Envelope/soap:Body/*/*[1]
/soap:Envelope/soap:Body/*/*[1]/*[1]
/soap:Envelope/soap:Body/*/*[1]/*[2]
/soap:Envelope/soap:Body/*/*[2]
/soap:Envelope/soap:Body/*/*[2]/*[1]
Как вы можете видеть все элементы внутри <soap:Body>
заменяются звездочками и индексами вместо имен элементов.
Запросы по этому пути работают, но мне будет нелегко их поддерживать, и я бы предпочел использовать вместо них имена элементов.
3 ответа
Другой вариант заключается в использованииlocal-name()
:
$xml = '<?xml version="1.0"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<SendPurchases xmlns="urn:services.insurance">
<Partner>
<UserID>MyCompany</UserID>
<Password>ABC123</Password>
</Partner>
<PurchasesRequest>
<Total>100</Total>
</PurchasesRequest>
</SendPurchases>
</soap:Body>
</soap:Envelope>';
$doc = new DOMDocument();
$doc->loadXML($xml);
$xpath = new DOMXPath($doc);
$elements = $xpath->query("//*[local-name()='UserID']");
var_dump($elements->item(0)->nodeValue);
// string(9) "MyCompany"
Спасибо @Dekel за совет. Мне нужно было зарегистрировать оба пространства имен, а затем указать пространство имен при запросе:
$xpath = new DOMXPath($doc);
$xpath->registerNamespace('soap', 'http://schemas.xmlsoap.org/soap/envelope/');
$xpath->registerNamespace('insr', 'urn:services.insurance');
$elements = $xpath->query('/soap:Envelope/soap:Body/insr:SendPurchases/insr:Partner/insr:UserID');
Теперь это работает.
Мое быстрое и грязное решение:
$path = $oNode->getNodePath(); #eg. /*/*[2]/*
while ($oNode) {
$path = preg_replace('~(.*/)[*]~', '$1'.$oNode->localName, $path, 1);
$oNode = $oNode->parentNode;
}
$path; #eg. /root/node[2]/subnode