2016-06-14 28 views
3

我有一個看起來像這樣的XML文檔:如何克隆PHP中沒有數據的獨特XML結構?

<root> 

    <node/> 

    <node> 
    <sub>more</sub> 
    </node> 

    <node> 
    <sub>another</sub> 
    </node> 

    <node>value</node> 

</root> 

這裏是我的僞代碼:

import xml. 

create empty-xml. 

foreach child of imported-xml-root-node, 

    recursively clone node structure without data. 

    if clone does not match one already in empty-xml, 
     then add clone to empty-xml. 

我想要得到的結果,看起來像這樣:

<root> 

    <node/> 

    <node> 
    <sub/> 
    </node> 

</root> 

注意我的piddly示例數據只有3個節點深。在生產中,會有未知數量的後代,因此可接受的答案需要處理變量節點深度。


失敗途徑

我已審查The DOMNode class具有cloneNode法,我想用遞歸選項,但它會採取一些額外的工作來清除數據。但是,雖然該類包含一個hasChildNodes函數,它返回一個布爾值,但是我找不到實際返回子集合的方法。

$doc = new DOMDocument(); 
$doc->loadXML($xml); 

$root_node = $doc->documentElement; 

if ($root_node->hasChildNodes()) { 

    // looking for something like this: 
    // foreach ($root_node->children() as $child) 
    // $doppel = $child->cloneNode(true); 

} 

其次,我已經試過我的手與The SimpleXMLElement class它確實有一個真棒children方法。雖然它沒有遞歸選項,但我建立了一個簡單的函數來克服它。但是這個類缺少一個clone/copyNode方法,而且我的函數膨脹成了一些令人討厭的補償。現在我正在考慮結合使用這兩個類,所以我可以訪問SimpleXMLElement::childrenDOMDocument::cloneNode,但我可以告訴這不是乾淨利落,當然這個問題可以更好地解決。

$sxe = new SimpleXMLElement($xml); 

$indentation = 0; 

function getNamesRecursive($xml, &$indentation) 
{ 
    $indentation++; 
    foreach($xml->children() as $child) { 
     for($i=0;$i<$indentation;$i++) 
      echo "\t"; 
     echo $child->getName() . "\n"; 
     getNamesRecursive($child,$indentation); 
    } 
    $indentation--; 
} 

getNamesRecursive($sxe,$indentation); 
+0

便攜式根節點的選擇:'$ doc-> documentElement' – rjdown

+0

@rjdown哦真棒,謝謝!我編輯了我的問題來解決這個問題。很高興知道! –

+1

[DOMNode :: $ childNodes](http://php.net/manual/de/class.domnode.php#domnode.props.childnodes)是屬性,而不是方法。 – ThW

回答

0

好吧,這裏是我的臭的解決方案。 suggestions for improvements或全新的更好的答案仍然非常受歡迎。

$xml = ' 
<root> 
    <node/> 
    <node> 
    <sub>more</sub> 
    </node> 
    <node> 
    <sub>another</sub> 
    </node> 
    <node>value</node> 
</root> 
'; 
$doc = new DOMDocument(); 
$doc->loadXML($xml); 


// clone without data 
$empty_xml = new DOMDocument(); 
$empty_xml->appendChild($empty_xml->importNode($doc->documentElement)); 
function clone_without_data(&$orig, &$clone, &$clonedoc){ 
    foreach ($orig->childNodes as $child){ 
    if(get_class($child) === "DOMElement") 
     $new_node = $clone->appendChild($clonedoc->importNode($child)); 
    if($child->hasChildNodes()) 
     clone_without_data($child,$new_node,$clonedoc); 
    } 
} 
clone_without_data($doc->documentElement, $empty_xml->documentElement, $empty_xml); 


// remove all duplicates 
$distinct_structure = new DOMDocument(); 
$distinct_structure->appendChild($distinct_structure->importNode($doc->documentElement)); 
foreach ($empty_xml->documentElement->childNodes as $child){ 
    $match = false; 
    foreach ($distinct_structure->documentElement->childNodes as $i => $element){ 
    if ($distinct_structure->saveXML($element) === $empty_xml->saveXML($child)) { 
     $match = true; 
     break; 
    } 
    } 
    if (!$match) 
    $distinct_structure->documentElement->appendChild($distinct_structure->importNode($child,true)); 
} 
$distinct_structure->formatOutput = true; 
echo $distinct_structure->saveXML(); 

導致這樣的輸出:

<?xml version="1.0"?> 
<root> 
    <node/> 
    <node> 
    <sub/> 
    </node> 
</root> 
1

考慮XSLT,設計來轉換XML文件中的專用語言。而PHP維護一個XSLT 1.0處理器。您只需保留位置1的項目並僅複製其元素而不是文本。

XSLT(保存爲文件的.xsl在PHP下面使用)

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
<xsl:output version="1.0" encoding="UTF-8" indent="yes" omit-xml-declaration="yes" /> 
<xsl:strip-space elements="*"/> 

    <!-- Identity Transform --> 
    <xsl:template match="@*|node()"> 
    <xsl:copy>  
     <xsl:apply-templates select="@*|node()"/>  
    </xsl:copy> 
    </xsl:template> 

    <!-- Remove any nodes position greater than 2 --> 
    <xsl:template match="*[position() &gt; 2]"/> 

    <!-- Copy only tags --> 
    <xsl:template match="/*/*/*"> 
    <xsl:copy/> 
    </xsl:template> 

</xsl:transform> 

PHP

// LOAD XML AND XSL FILES 
$xml = new DOMDocument('1.0', 'UTF-8'); 
$xml->load('Input.xml'); 

$xslfile = new DOMDocument('1.0', 'UTF-8'); 
$xslfile->load('Script.xsl'); 

// TRANSFORM XML with XSLT 
$proc = new XSLTProcessor; 
$proc->importStyleSheet($xslfile); 
$newXml = $proc->transformToXML($xml); 

// ECHO OUTPUT STRING 
echo $newXml; 
# <root> 
# <node/> 
# <node> 
#  <sub/> 
# </node> 
# </root> 

// NEW DOM OBJECT 
$final = new DOMDocument('1.0', 'UTF-8'); 
$final->loadXML($newXml); 
+0

哇,這看起來很簡單。我迫不及待地想要在早上試試它:)謝謝!順便說一句,你是怎麼偶然發現我的問題?我確信它現在被埋了。 –

+0

它適合你嗎? – Parfait

+0

我真的很喜歡這種優雅,所以我現在對XSL有很多瞭解,然而,似乎'match =「/ */*/*」'限制爲3個節點。只使用'*'只能給我一個根。我現在正在修補「刪除大於2的任何節點位置」部分。我的實際使用案例將有未知數量的節點深度,因此是我的遞歸策略。 –