在递归函数中获取xml节点的xpath

时间:2010-02-22 15:53:17

标签: php xml xpath simplexml

假设我有一些代码可以递归迭代遍历XML文件:

$xmlfile = new SimpleXMLElement('http://www.domain.com/file.xml',null,true);
xmlRecurse($xmlfile,0);
function xmlRecurse($xmlObj,$depth) {
  foreach($xmlObj->children() as $child) {
    echo str_repeat('-',$depth).">".$child->getName().": ".$subchild."\n";
    foreach($child->attributes() as $k=>$v){
        echo "Attrib".str_repeat('-',$depth).">".$k." = ".$v."\n";
    }
    xmlRecurse($child,$depth+1);
  }
}

我如何计算每个节点的xpath,以便我可以存储它以映射到其他代码?

5 个答案:

答案 0 :(得分:5)

显而易见的方法是将XPath作为第三个参数传递,并在深入挖掘时构建它。您必须考虑具有相同名称的兄弟姐妹,因此您必须在迭代时跟踪与当前孩子同名的先例兄弟姐妹的数量。

工作示例:

function xmlRecurse($xmlObj,$depth=0,$xpath=null) {
  if (!isset($xpath)) {
    $xpath='/'.$xmlObj->getName().'/';
  }
  $position = array();

  foreach($xmlObj->children() as $child) {

    $name = $child->getName();
    if(isset($position[$name])) {
      ++$position[$name];
    }
    else {
      $position[$name]=1;
    }
    $path=$xpath.$name.'['.$position[$name].']';

    echo str_repeat('-',$depth).">".$name.": $path\n";
    foreach($child->attributes() as $k=>$v){
        echo "Attrib".str_repeat('-',$depth).">".$k." = ".$v."\n";
    }

    xmlRecurse($child,$depth+1,$path.'/');
  }
}

但是注意,映射整个文档并沿途存储XPath的整个想法似乎很奇怪。实际上,您可能正在针对完全不同的问题制定错误的解决方案。

答案 1 :(得分:3)

您可以传递给名为$ xpath的xmlRecurse第三个参数(使用当前节点xPath表示),并在每次迭代时添加子项的xpath表示:

function xmlRecurse($xmlObj,$depth,$xpath) {
  $i=0;
  foreach($xmlObj->children() as $child) {
    echo str_repeat('-',$depth).">".$child->getName().": ".$subchild."\n";
    foreach($child->attributes() as $k=>$v){
        echo "Attrib".str_repeat('-',$depth).">".$k." = ".$v."\n";
    }
    xmlRecurse($child,$depth+1,$xpath.'/'.$child->getName().'['.$i++.']');
  }
}

答案 2 :(得分:2)

使用SimpleXML,我认为你只能像其他人指出的那样:通过将节点路径作为字符串参数递归。

使用DOMDocument,您可以使用$node->parentNode属性爬回文档元素并为任意节点构建它(例如,如果您有一个节点的引用并想要发现它在树中的位置没有事先了解你如何到达那个节点。)

答案 3 :(得分:2)

$domNode = dom_import_simplexml($node);
$xpath = $domNode->getNodePath();

你需要PHP 5> = 5.2.0才能工作。

答案 4 :(得分:1)

跟进MightyE关于回溯的想法:

function whereami($node)
{
    if ($node instanceof SimpleXMLElement)
    {
        $node = dom_import_simplexml($node);
    }
    elseif (!$node instanceof DOMNode)
    {
        die('Not a node?');
    }

    $q     = new DOMXPath($node->ownerDocument);
    $xpath = '';

    do
    {
        $position = 1 + $q->query('preceding-sibling::*[name()="' . $node->nodeName . '"]', $node)->length;
        $xpath    = '/' . $node->nodeName . '[' . $position . ']' . $xpath;
        $node     = $node->parentNode;
    }
    while (!$node instanceof DOMDocument);

    return $xpath;
}

我不建议将其用于手头的情况(映射整个文档,而不是单个给定节点),但它可能对将来参考有用。