2010-02-22 142 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才能正常工作。