PHP XML - Find out the path to a known value

2019-07-23 08:07发布

Here is an XML bit:

[11] => SimpleXMLElement Object
                                (
                                    [@attributes] => Array
                                        (
                                            [id] => 46e8f57e67db48b29d84dda77cf0ef51
                                            [label] => Publications
                                        )

                                    [section] => Array
                                        (
                                            [0] => SimpleXMLElement Object
                                                (
                                                    [@attributes] => Array
                                                        (
                                                            [id] => 9a34d6b273914f18b2273e8de7c48fd6
                                                            [label] => Journal Articles
                                                            [recordId] => 1a5a5710b0e0468e92f9a2ced92906e3
                                                        )

I know the value "46e8f57e67db48b29d84dda77cf0ef51" but its location varies across files. Can I use XPath to find the path to this value? If not what could be used?

Latest trial that does not work:

$search = $xml->xpath("//text()=='047ec63e32fe450e943cb678339e8102'");

while(list( , $node) = each($search)) {

    echo '047ec63e32fe450e943cb678339e8102',$node,"\n";

}

2条回答
戒情不戒烟
2楼-- · 2019-07-23 08:36

Is this string always in the @id attribute? Then a valid and distinct path is always //*[@id='46e8f57e67db48b29d84dda77cf0ef51'], no matter where it is.

To construct a path to a given node, use $node->getNodePath() which will return an XPath expression for the current node. Also take this answer on constructing XPath expression using @id attributes, similar to like Firebug does, in account.

For SimpleXML you will have to do everything by hand. If you need to support attribute and other paths, you will have to add this, this code only supports element nodes.

$results = $xml->xpath("/highways/route[66]");

foreach($results as $result) {
    $path = "";
    while (true) {
        // Is there an @id attribute? Shorten the path.
        if ($id = $result['id']) {
            $path = "//".$result->getName()."[@id='".(string) $id."']".$path;
            break;
        }
        // Determine preceding and following elements, build a position predicate from it.
        $preceding = $result->xpath("preceding-sibling::".$result->getName());
        $following = $result->xpath("following-sibling::".$result->getName());
        $predicate = (count($preceding) + count($following)) > 0 ? "[".(count($preceding)+1)."]" : "";
        $path = "/".$result->getName().$predicate.$path;
        // Is there a parent node? Then go on.
        $result = $result->xpath("parent::*");
        if (count($result) > 0) $result = $result[0];
        else break;
    }
    echo $path."\n";
}
查看更多
Explosion°爆炸
3楼-- · 2019-07-23 08:54

PHPs DOMNode objects have a function for that: DOMNode::getNodePath()

$xml = <<<'XML'
<root>
  <child key="1">
    <child key="2"/>
    <child key="3"/>
  </child>
</root>
XML;

$dom = new DOMDocument();
$dom->loadXml($xml);
$xpath = new DOMXpath($dom);

$nodes = $xpath->evaluate('//child');

foreach ($nodes as $node) {
  var_dump($node->getNodePath());
}

Output:

string(11) "/root/child"
string(20) "/root/child/child[1]"
string(20) "/root/child/child[2]"

SimpleXML is a wrapper for DOM and here is a function that allows you to get the DOMNode for an SimpleXMLElement: dom_import_simplexml.

$xml = <<<'XML'
<root>
  <child key="1">
    <child key="2"/>
    <child key="3"/>
  </child>
</root>
XML;

$structure = simplexml_load_string($xml);
$elements = $structure->xpath('//child');

foreach ($elements as $element) {
  $node = dom_import_simplexml($element);
  var_dump($node->getNodePath());
}

To fetch an element by its attribute xpath can be used.

Select all nodes using the element joker anywhere in the document:

//*

Filter them by the id attribute:

//*[@id = "46e8f57e67db48b29d84dda77cf0ef51"]

$dom = new DOMDocument();
$dom->loadXml('<node id="46e8f57e67db48b29d84dda77cf0ef51"/>');
$xpath = new DOMXpath($dom);

foreach ($xpath->evaluate('//*[@id = "46e8f57e67db48b29d84dda77cf0ef51"]') as $node) {
  var_dump(
    $node->getNodePath()
  );
}
查看更多
登录 后发表回答