2016-07-14 2 views
0

У меня есть документ XML с элементом (с именем region), который может содержать или не иметь несколько дочерних элементов. Для моего импорта мне нужно убедиться, что есть только один дочерний элемент. Если есть больше дочерних элементов, мне нужно удалить всех детей, кроме последнего. Следующие ситуации могут возникнуть:Манипулировать дочерние элементы с xPath в PHP

// 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> 

Что мне нужно, чтобы отфильтровать XML переформатировать дочерние элементы region элемента всегда иметь один дочерний элемент (value) с эфиром значение последнего дочернего элемента или если нет детей, значение «нет».

Мой желаемый результат будет следующим:

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

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

Я знаю, что я могу выбрать region свойство с XPATH запроса //property[@name = "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 Loop в сочетании с с getElementByTagName() из класс DOMElement. Вот как:

<?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 Файл должен выглядеть примерно так:

<?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) 

Надежда это здесь может дать вам представление о том, как сделать это лучше всего по своему усмотрению.

Приветствия и удачи !!!

+0

То есть именно то, что мне нужно! Благодаря! – Maurice

0

У каждого узла есть свойство с именем childNodes, которое имеет свойство length, что означает, что вы можете иметь количество дочерних узлов текущего узла таким образом. Но поскольку белые пробелы рассматриваются как один узел, вы должны инициализировать другой запрос и вычесть их из childNodes->length.

Это решение является правильным, но это не самым лучшим, как мы можем сделать его коротким:

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

Это означает, что все узлы элементов в пределах текущего узла (не белое-пространство). После нахождения элементов с более чем одного ребенка элемента, мы почти сделали:

./*[position() < last()] 

Что означает, что все дети с позиции меньше, чем последний в текущем узле. После этого мы можем легко их удалить.Вот ваш модифицированный foreach цикл:

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(); 
Смежные вопросы