In SimpleXML, how can I add an existing SimpleXMLElement as a child?

I have a SimpleXMLElement $ child object and a SimpleXMLElement $ parent object.

How to add $ child as a parent of $ parent? Is there a way to do this without converting to the DOM and vice versa?

The addChild () method only allows me to create a new empty element, but this does not help when the element I want to add $ child also has children. I think I might need recursion.

+6
oop php simplexml
source share
4 answers

I know that this is not the most useful answer, but especially since you are creating / modifying XML, I would switch to using DOM functions. SimpleXML is good for accessing simple documents, but pretty poor at changing them.

If SimpleXML handles you kindly in all other places, and you want to stick with it, you have the option to go to the DOM functions temporarily to do what you need and then go back again using dom_import_simplexml() and simplexml_import_dom() . I'm not sure how effective this is, but it can help you.

+6
source share

Unfortunately, SimpleXMLElement does not offer anything to combine the two elements. As @nickf wrote , it is more suitable for reading than for manipulation. However, the DOMDocument sister DOMDocument is for editing, and you can combine them together via dom_import_simplexml() . And @salathe shows in the corresponding answer how this works for certain SimpleXMLElements.

The following shows how this works with input validation and some additional parameters. I am doing this with two examples. The first example is a function to insert an XML string:

 /** * 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); } 

This sample function allows you to add XML or insert it before a specific element, including the root element. Upon learning whether there is anything to add, it uses the functions and methods of DOMDocument to insert XML as a fragment of a document, also outlined in How to Import an XML String into PHP DOMDocument . Usage example:

 $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(); 

This gives the following result:

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

The second example is the insertion of SimpleXMLElement . If necessary, it uses the first function. It basically checks if there is anything to do at all and which item should be imported. If it is an attribute, it will simply add it; if it is an element, it will be serialized in XML and then added to the parent element as 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); } 

This sample function normalizes the list of elements and attributes, such as common in Simplexml. You might want to change it to insert several SimpleXMLElements at once, but as the usage example shows, my example does not support this (see the Attributes example):

 // 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(); 

This is a continuation of the first use case. Therefore, the output is now:

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

Hope this is helpful. You can find the code in essence and an online demo / Overview of the PHP version .

+7
source share

In fact, it is possible (dynamically) if you look closely at how addChild() is defined. I used this technique to convert any array to XML using recursion and pass by reference

  • addChild() returns the SimpleXMLElement added child.
  • add a node sheet, use $xml->addChilde($nodeName, $nodeValue) .
  • to add a node, which may have a sub-sub or value, use $xml->addChilde($nodeName) , addChild() not passed. This will result in a subnode of type SimpleXMLElement! not a line!

XML target

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

The code:

 $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'); 
+1
source share

Leaving this here when I just stumbled across this page and found that SimpleXML now supports this functionality with the :: addChild method .

You can use this method to add any cascading elements:

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

All Articles