I currently need a an XPath path to an element. The path must contain an attribute that uniquely identifies each element along the path (In this case, the name attribute).
$DOMDocument->getNodePath();
returns the path, but no attributes. The attributes are neccessary as the XML will be changing quite a bit.
I am currently writing a method to manually go through and check each element in the path for the name attribute, then inserting it into the string at the appropriate point. Is there an easier/faster/not as hackish way of doing this?
The xsh solution from @choroba can be translated to PHP source using DOMXpath::evaluate()
:
$document = new DOMDocument();
$document->loadXml($xml);
$xpath = new DOMXpath($document);
foreach ($xpath->evaluate('//address') as $address) {
$path = '';
foreach ($xpath->evaluate('ancestor-or-self::*', $address) as $ancestor) {
$path .= '/'.$ancestor->nodeName;
if ($name = $ancestor->getAttribute('name')) {
$path .= '[@name = "'.$name.'"]';
}
}
var_dump($path);
}
ancestor-or-self::*
is an Xpath expression that fetches all element nodes starting from the context node up to the document element.
You might be able to use XPath to get the string. For example, in xsh:
for //address {
for ancestor-or-self::*
echo :s :n '/'
name()
xsh:if(@name, concat('[@name="', @name, '"]'),'') ;
echo ;
}
Runnnin it on the following XML
<root>
<customers name="2014">
<customer name="John">
<address/>
</customer>
<customer name="Jane">
<address/>
</customer>
</customers>
<customers name="2015">
<customer name="Daniel">
<address/>
</customer>
<customer name="Dave">
<address/>
</customer>
</customers>
</root>
I'm getting
/root/customers[@name="2014"]/customer[@name="John"]/address
/root/customers[@name="2014"]/customer[@name="Jane"]/address
/root/customers[@name="2015"]/customer[@name="Daniel"]/address
/root/customers[@name="2015"]/customer[@name="Dave"]/address