Я имею XML:
<root> <nodeA /> <nodeA /> <nodeA /> <nodeC /> <nodeC /> <nodeC /> </root>
Как вставить «nodeB» между As и Cs? В PHP, желательно через SimpleXML? Подобно:
<root> <nodeA /> <nodeA /> <nodeA /> <nodeB /> <nodeC /> <nodeC /> <nodeC /> </root>
Ниже приведена функция вставки нового элемента 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();
Если вам нужно / нужно объяснение того, как это работает (код довольно простой, но может включать в себя зарубежные концепции), просто спросите.
Ответ Салате помог мне, но так как я использовал метод 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, который позволяет указать точный элемент для вставки элемента раньше, но поскольку мне это не нужно прямо сейчас, я решил не делать этого.