PHP SimpleXML: вставить узел в определенную позицию - PullRequest
9 голосов
/ 29 июля 2010

говорят, что у меня есть XML:

<root>
  <nodeA />
  <nodeA />
  <nodeA />
  <nodeC />
  <nodeC />
  <nodeC />
</root>

Как вставить "узел B" между As и Cs?В PHP желательно через SimpleXML?Нравится:

<root>
  <nodeA />
  <nodeA />
  <nodeA />
  <nodeB />
  <nodeC />
  <nodeC />
  <nodeC />
</root>

Ответы [ 2 ]

16 голосов
/ 29 июля 2010

Ниже приведена функция для вставки нового SimpleXMLElement после некоторого другого SimpleXMLElement. Поскольку с SimpleXML это невозможно сделать напрямую, он использует закулисные классы / методы DOM для выполнения работы.

function simplexml_insert_after(SimpleXMLElement $insert, SimpleXMLElement $target)
{
    $target_dom = dom_import_simplexml($target);
    $insert_dom = $target_dom->ownerDocument->importNode(dom_import_simplexml($insert), true);
    if ($target_dom->nextSibling) {
        return $target_dom->parentNode->insertBefore($insert_dom, $target_dom->nextSibling);
    } else {
        return $target_dom->parentNode->appendChild($insert_dom);
    }
}

И пример того, как это можно использовать (специфично для вашего вопроса):

$sxe = new SimpleXMLElement('<root><nodeA/><nodeA/><nodeA/><nodeC/><nodeC/><nodeC/></root>');
// New element to be inserted
$insert = new SimpleXMLElement("<nodeB/>");
// Get the last nodeA element
$target = current($sxe->xpath('//nodeA[last()]'));
// Insert the new element after the last nodeA
simplexml_insert_after($insert, $target);
// Peek at the new XML
echo $sxe->asXML();

Если вы хотите / нуждаетесь в объяснении , как это работает (код довольно прост, но может включать в себя иностранные понятия), просто спросите.

4 голосов
/ 01 июня 2011

Ответ Салате мне помог, но так как я использовал метод addChild в SimpleXMLElement, я искал решение, чтобы сделать вставку потомков в качестве первого потомка более прозрачной. Решение состоит в том, чтобы взять эту функциональность на основе DOM и скрыть ее в подклассе SimpleXMLElement:

class SimpleXMLElementEx extends SimpleXMLElement
{
    public function insertChildFirst($name, $value, $namespace)
    {
        // Convert ourselves to DOM.
        $targetDom = dom_import_simplexml($this);
        // Check for children
        $hasChildren = $targetDom->hasChildNodes();

        // Create the new childnode.
        $newNode = $this->addChild($name, $value, $namespace);

        // Put in the first position.
        if ($hasChildren)
        {
            $newNodeDom = $targetDom->ownerDocument->importNode(dom_import_simplexml($newNode), true);
            $targetDom->insertBefore($newNodeDom, $targetDom->firstChild);
        }

        // Return the new node.
        return $newNode;
    }
}

В конце концов, SimpleXML позволяет указать, какой класс элементов использовать:

$xml = simplexml_load_file($inputFile, 'SimpleXMLElementEx');

Теперь вы можете вызывать insertChildFirst для любого элемента, чтобы вставить новый дочерний элемент в качестве первого дочернего элемента. Метод возвращает новый элемент как элемент SimpleXML, поэтому его использование аналогично addChild. Конечно, было бы легко создать метод insertChild, который позволяет указывать точный элемент для вставки элемента раньше, но, поскольку мне это сейчас не нужно, я решил этого не делать.

...