2016-07-14 13 views
0

子要素が複数ある場合とない場合がある要素(名前はregion)を持つXML文書があります。私のインポートでは、子要素が1つだけであることを確認する必要があります。より多くの子要素がある場合は、最後の要素を除くすべての子要素を削除する必要があります。次のような状況が発生する可能性がありますPHPでxPathを使用して子要素を操作する

// Option 1: No child elements 
<property name="region"> 
</property> 

// Option 2: One child element 
<property name="region"> 
<value>Bottom</value> 
</property> 

// Option 3: Two child elements 
<property name="region"> 
<value>Top</value> 
<value>Bottom</value> 
</property> 

// Option 4: Three child elements 
<property name="region"> 
<value>Top</value> 
<value>Middle</value> 
<value>Bottom</value> 
</property> 

を私は必要なものは常に1つの子要素(value)エーテル最後の子要素の値を持つ場合、またはを持つようにregion要素の子要素をフォーマットし直すためにXMLをフィルタリングすることです値が 'none'の子はありません。

私の所望の出力は以下のようになります:

// For Option 1 
<property name="region"> 
<value>none</value> 
</property> 

// For option 2, 3 & 4 
<property name="region"> 
<value>Bottom</value> 
</property> 

私は私がXPathクエリ//property[@name = "region"]regionプロパティを選択することができます知っている、しかし、私はそこから子どもたちを操作する方法がわかりません。私は動けなくなるとき

は、私は、次のコードを持っている

<?php 
$xml = '<properties> 
     <property name="region"> 
     </property> 

     <property name="region"> 
     <value>Bottom</value> 
     </property> 

     <property name="region"> 
     <value>Top</value> 
     <value>Bottom</value> 
     </property> 

     <property name="region"> 
     <value>Top</value> 
     <value>Middle</value> 
     <value>Bottom</value> 
     </property> 
</properties>'; 
$document = new DOMDocument(); 
$document->loadXML($xml); 
$xpath = new DOMXpath($document); 
foreach($xpath->query('//property[@name = "region"]') as $node){ 

     // Now i need something like below, but i can't find a way to make it work 
     if $node->hasChildren() 
      Remove all but last child 
     else 
      Create child element with text none 
} 

私は誰かが

答えて

0

あなたが行うことができ、正しい方向に私を指すことができます願って一緒にねじれた、ネストされたforループとのgetElementByTagName()ととDOMElementクラス。

<?xml version="1.0"?> 
    <properties> 
     <property name="region"> 
     <value>none</value> 
     </property> 

     <property name="region"> 
     <value>Bottom</value> 
     </property> 

     <property name="region">   
     <value>Bottom</value> 
     </property> 

     <property name="region">   
     <value>Bottom</value>   
     </property> 
    </properties> 

var_dump($document);の結果はこれと似たような生成する必要がありますが:

object(DOMDocument)[1] 
     public 'doctype' => null 
     public 'implementation' => string '(object value omitted)' (length=22) 
     public 'documentElement' => string '(object value omitted)' (length=22) 
     public 'actualEncoding' => null 
     public 'encoding' => null 
     public 'xmlEncoding' => null 
     public 'standalone' => boolean true 
     public 'xmlStandalone' => boolean true 
     public 'version' => string '1.0' (length=3) 
     public 'xmlVersion' => string '1.0' (length=3) 
     public 'strictErrorChecking' => boolean true 
     public 'documentURI' => string '/Applications/MAMP/htdocs/poiz/so/' (length=34) 
     public 'config' => null 
     public 'formatOutput' => boolean false 
     public 'validateOnParse' => boolean false 
     public 'resolveExternals' => boolean false 
     public 'preserveWhiteSpace' => boolean true 
     public 'recover' => boolean false 
     public 'substituteEntities' => boolean false 
     public 'nodeName' => string '#document' (length=9) 
     public 'nodeValue' => null 
     public 'nodeType' => int 9 
     public 'parentNode' => null 
     public 'childNodes' => string '(object value omitted)' (length=22) 
     public 'firstChild' => string '(object value omitted)' (length=22) 
     public 'lastChild' => string '(object value omitted)' (length=22) 
     public 'previousSibling' => null 
     public 'attributes' => null 
     public 'ownerDocument' => null 
     public 'namespaceURI' => null 
     public 'prefix' => string '' (length=0) 
     public 'localName' => null 
     public 'baseURI' => string '/Applications/MAMP/htdocs/poiz/so/' (length=34) 
     public 'textContent' => string ' 

       none 

       Bottom 

       Bottom 

       Bottom 

     ' (length=146) 

希望を

<?php 
    $xml = '<properties> 
     <property name="region"> 
     </property> 

     <property name="region"> 
     <value>Bottom</value> 
     </property> 

     <property name="region"> 
     <value>Top</value> 
     <value>Bottom</value> 
     </property> 

     <property name="region"> 
     <value>Top</value> 
     <value>Middle</value> 
     <value>Bottom</value> 
     </property> 
</properties>'; 

    $document   = new DOMDocument(); 
    $document->loadXML($xml); 
    $xpath    = new DOMXpath($document); 

    foreach($xpath->query('//property[@name = "region"]') as $node){ 
     /**@var DOMElement $node*/ 
     // CHECK IF THE $node HAS CHILD NODES USING $node->getElementsByTagName("value")->length PROPERTY 
     // WHICH RETURNS THE NUMBER OF CHILD NODES 
     $numChildNodes  = $node->getElementsByTagName("value")->length; 

     // IF THE CURRENT NODE HAS AT LEAST 1 CHILD, LOOP THROUGH THE CHILDREN 
     // AND REMOVE THE CHILD NODES...EXCEPT FOR THE LAST CHILD NODE... 
     if($numChildNodes > 0){ 
      $cuePoint  = ($numChildNodes - 1); 

      // LOOP THROUGH ALL CHILD-NODES OF THE CURRENT NODE AND REMOVE ALL CHILD NODES 
      // EXCEPT FOR THE LAST CHILD NODE... 
      for($index=0; $index<$numChildNodes; $index++){ 
       if($index !== $cuePoint) { 
        $currentNode = $node->getElementsByTagName("value")->item($index); 
        // REMOVE THIS NODE... 
        $node->removeChild($currentNode); 
       } 
      } 
     }else{ 
      // IF THE CURRENT NODE HAS NO CHILD AT ALL, 
      // SIMPLY CREATE AN ELEMENT NODE AND APPEND IT IN THE RIGHT CONTEXT... 
      // HERE WE ARE USING none AS DEFAULT BUT YOU CAN USE WHATEVER 
      // STRING YOU PLEASE ... 
      $newElementNode  = new DOMElement("value", "none", ""); 
      $node->appendChild($newElementNode); 
     } 
    } 
    $document->save("abc.xml"); 
    var_dump($document); 

abc.xmlファイルは次のようになります。ここでは方法ですこれはここであなた自身で最高のやり方を知ることができます。

乾杯!

+0

ここで変更したforeachループがあります!ありがとう! – Maurice

0

各ノードにはlengthプロパティを持つchildNodesという名前のプロパティがあります。つまり、現在のノードの子ノードの数をこのようにすることができます。しかし、空白は単一ノードとみなされるので、別のクエリを初期化してchildNodes->lengthから引き算する必要があります。

このソリューションは正しいですが、我々はそれを短くすることができますよう最善ではありません:これは、現在のノード(ないホワイトスペース)内のすべての要素ノードを意味

$xpath->query('./*', $node)->length > 1 

。複数の要素を持つ要素を見つけたら、ほぼ完了です。

これは、現在のノード内の最後の位置よりも小さい位置の子をすべて意味します。あとで簡単に取り除くことができます。

foreach ($query as $node){ 
    if ($xpath->query('./*', $node)->length > 1) { 
     $children = $xpath->query('./*[position() < last()]', $node); 
     foreach ($children as $child) { 
      $child->parentNode->removeChild($child); 
     } 
    } 
} 

あなたは出力を確認することができます変更を保存して:まさに私が必要とするものである

echo $document->saveXML(); 
関連する問題