【问题标题】:Get xpath of xml node within recursive function在递归函数中获取xml节点的xpath
【发布时间】:2011-01-19 16:53:10
【问题描述】:

假设我有一些代码可以像这样递归地遍历 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,以便将其存储以映射到其他代码?

【问题讨论】:

  • 计算xpath是什么意思?
  • 我的意思是为每个 $child 计算它的 xpath - 例如/dwml/data/location/point/@latitude

标签: php xml xpath simplexml


【解决方案1】:
$domNode = dom_import_simplexml($node);
$xpath = $domNode->getNodePath();

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

【讨论】:

    【解决方案2】:

    跟进 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;
    }
    

    我不推荐它用于手头的案例(映射整个文档,而不是单个给定节点),但它可能对将来的参考有用。

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

        【解决方案4】:

        显而易见的方法是将 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 的整个想法似乎很奇怪。实际上,您可能正在为一个完全不同的问题寻找错误的解决方案。

        【讨论】:

        • 有趣。我们实际上正在做的是允许用户上传模板 XML 文件,我们需要在我们的系统中存储每个节点/属性映射到的映射。你能推荐一个比 xpath 更好的方法吗?我真正喜欢它的是一个简单的字符串存储路径的想法
        【解决方案5】:

        您可以将名为 $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++.']');
          }
        }
        

        【讨论】:

        • 当然,您也可以使用其属性构建当前子 xPath 表示。但是这样你必须将所有 xpath 字符串存储在数组中,以确保你没有添加重复项
        • 那是真的,我想知道是否有更直接的东西而不依赖于通过递归函数传递变量,比如 $child->current() 或类似的东西
        猜你喜欢
        • 2012-01-20
        • 2014-08-17
        • 1970-01-01
        • 1970-01-01
        • 2011-10-28
        • 2014-12-25
        • 1970-01-01
        • 2016-01-28
        • 1970-01-01
        相关资源
        最近更新 更多