В SimpleXML, как я могу добавить существующий SimpleXMLElement в качестве дочернего элемента?

У меня есть объект SimpleXMLElement $ child и объект $ SimpleXMLElement.

Как я могу добавить $ child в качестве родителя $ parent? Есть ли способ сделать это без преобразования в DOM и обратно?

Метод addChild () только позволяет мне создать новый пустой элемент, но это не помогает, когда элемент, который я хочу добавить $ child, также имеет дочерние элементы. Я думаю, мне может понадобиться рекурсия.

Я знаю, что это не самый полезный ответ, но тем более, что вы создаете / изменяете XML, я бы переключился на использование функций DOM. SimpleXML хорош для доступа к простым документам, но довольно беден при их изменении.

Если SimpleXML обрабатывает вас любезно во всех других местах, и вы хотите придерживаться его, вы все равно можете переходить к функциям DOM временно, чтобы выполнить то, что вам нужно, а затем снова вернуться назад, используя dom_import_simplexml() и simplexml_import_dom() . Я не уверен, насколько это эффективно, но это может помочь вам.

К сожалению, SimpleXMLElement не предлагает ничего, чтобы объединить два элемента. Как писал @nickf , он более подходит для чтения, чем для манипуляций. Тем не менее, DOMDocument расширений для редактирования, и вы можете объединить их вместе через dom_import_simplexml() . И @salathe показывает в связанном ответе, как это работает для конкретных SimpleXMLElements.

Ниже показано, как это работает с проверкой ввода и некоторыми дополнительными параметрами. Я делаю это с двумя примерами. Первый пример – это функция для вставки строки XML:

 /** * Insert XML into a SimpleXMLElement * * @param SimpleXMLElement $parent * @param string $xml * @param bool $before * @return bool XML string added */ function simplexml_import_xml(SimpleXMLElement $parent, $xml, $before = false) { $xml = (string)$xml; // check if there is something to add if ($nodata = !strlen($xml) or $parent[0] == NULL) { return $nodata; } // add the XML $node = dom_import_simplexml($parent); $fragment = $node->ownerDocument->createDocumentFragment(); $fragment->appendXML($xml); if ($before) { return (bool)$node->parentNode->insertBefore($fragment, $node); } return (bool)$node->appendChild($fragment); } 

Эта примерная функция позволяет добавлять XML или вставлять его перед определенным элементом, включая корневой элемент. Узнав, есть ли что-то, что нужно добавить, он использует функции и методы DOMDocument для вставки XML в качестве фрагмента документа, а также в том, как импортировать XML-строку в PHP DOMDocument . Пример использования:

 $parent = new SimpleXMLElement('<parent/>'); // insert some XML simplexml_import_xml($parent, "\n <test><this>now</this></test>\n"); // insert some XML before a certain element, here the first <test> element // that was just added simplexml_import_xml($parent->test, "<!-- leave a comment -->\n ", $before = true); // you can place comments above the root element simplexml_import_xml($parent, "<!-- this works, too -->", $before = true); // but take care, you can produce invalid XML, too: // simplexml_add_xml($parent, "<warn><but>take care!</but> you can produce invalid XML, too</warn>", $before = true); echo $parent->asXML(); 

Это дает следующий результат:

 <?xml version="1.0"?> <!-- this works, too --> <parent> <!-- leave a comment --> <test><this>now</this></test> </parent> 

Второй пример – вставка SimpleXMLElement . При необходимости он использует первую функцию. Он в основном проверяет, есть ли что-то делать вообще и какой элемент должен быть импортирован. Если это атрибут, он просто добавит его, если это элемент, он будет сериализован в XML и затем добавлен в родительский элемент как XML:

 /** * Insert SimpleXMLElement into SimpleXMLElement * * @param SimpleXMLElement $parent * @param SimpleXMLElement $child * @param bool $before * @return bool SimpleXMLElement added */ function simplexml_import_simplexml(SimpleXMLElement $parent, SimpleXMLElement $child, $before = false) { // check if there is something to add if ($child[0] == NULL) { return true; } // if it is a list of SimpleXMLElements default to the first one $child = $child[0]; // insert attribute if ($child->xpath('.') != array($child)) { $parent[$child->getName()] = (string)$child; return true; } $xml = $child->asXML(); // remove the XML declaration on document elements if ($child->xpath('/*') == array($child)) { $pos = strpos($xml, "\n"); $xml = substr($xml, $pos + 1); } return simplexml_import_xml($parent, $xml, $before); } 

Эта примерная функция выполняет нормализацию списка элементов и атрибутов, таких как common в Simplexml. Возможно, вы захотите изменить его, чтобы вставить сразу несколько SimpleXMLElements, но, как показывает пример использования, мой пример не поддерживает это (см. Пример атрибутов):

 // append the element itself to itself simplexml_import_simplexml($parent, $parent); // insert <this> before the first child element (<test>) simplexml_import_simplexml($parent->children(), $parent->test->this, true); // add an attribute to the document element $test = new SimpleXMLElement('<test attribute="value" />'); simplexml_import_simplexml($parent, $test->attributes()); echo $parent->asXML(); 

Это продолжение первого примера использования. Таким образом, теперь результат:

 <?xml version="1.0"?> <!-- this works, too --> <parent attribute="value"> <!-- leave a comment --> <this>now</this><test><this>now</this></test> <!-- this works, too --> <parent> <!-- leave a comment --> <test><this>now</this></test> </parent> </parent> 

Надеюсь, это полезно. Вы можете найти код в формате gist и в виде онлайн-демонстрационной версии / версии PHP .

На самом деле, возможно (динамически), если вы внимательно посмотрите, как определяется addChild() . Я использовал эту технику для преобразования любого массива в XML с использованием рекурсии и передачи по ссылке

  • addChild() возвращает SimpleXMLElement добавленного дочернего addChild() .
  • для добавления листового узла используйте $xml->addChilde($nodeName, $nodeValue) .
  • для добавления узла, который может иметь субнод или значение, используйте $xml->addChilde($nodeName) , никакое значение не передается addChild() . Это приведет к тому, что будет существовать субнод типа SimpleXMLElement! не строка!

целевой XML

 <root> <node>xyz</node> <node> <node>aaa</node> <node>bbb</node> </node> </root> 

Код:

 $root = new SimpleXMLElement('<root />'); //add child with name and string value. $root.addChild('node', 'xyz'); //adds child with name as root of new SimpleXMLElement $sub = $root->addChild('node'); $sub.addChild('node', 'aaa'); $sub.addChild('node', 'bbb'); 

Оставляя это здесь, когда я просто наткнулся на эту страницу и обнаружил, что SimpleXML теперь поддерживает эту функцию с помощью метода :: addChild .

Вы можете использовать этот метод, чтобы добавить любые каскадные элементы:

 $xml->addChild('parent'); $xml->parent->addChild('child'); $xml->parent->child->addChild('child_id','12345'); , $xml->addChild('parent'); $xml->parent->addChild('child'); $xml->parent->child->addChild('child_id','12345');