2010-02-22 146 views
3

中获取XML节点的XPath可以说我有一些代码通过递归这样一个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的,所以我可以将其存储映射到其他代码?

+0

通过计算xpath,你的意思是什么? – Gordon 2010-02-22 15:57:49

+0

我的意思是每个孩子计算其xpath - 例如/ dwml/data/location/point/@ latitude – robjmills 2010-02-22 16:01:35

回答

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的整体思路似乎不可思议。你可能实际上正在为完全不同的问题研究错误的解决方案。

+0

有趣。我们实际上在看的是允许用户上传模板XML文件,我们需要存储每个节点/属性映射到我们系统内的映射。你能推荐比xpath更好的方法吗?我最喜欢的是真正的存储路径的简单字符串的想法 – robjmills 2010-02-22 16:16:38

3

你可以传给你的xmlRecurse第三PARAM名为$的XPath(与当前节点的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++.']'); 
    } 
} 
+0

当然,您还可以使用其属性构建当前的子xPath表示形式。但这样你必须存储所有XPath字符串数组,以确保你没有添加重复 – Ololo 2010-02-22 16:04:23

+0

这是真的,我想知道是否有更直接的东西,而不是依赖递归函数传递变量,如$ child- > current()或类似的东西 – robjmills 2010-02-22 16:05:03

2

对于SimpleXML,我认为你只能这样做,因为别人指出:通过递归节点路径作为字符串参数。

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

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

我手头没有推荐它的情况下(映射整个文档,而不是一个单一的给定节点),但它可能是有用的以后的参考。

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

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