I\'m trying to build a rather complex XML document.
I have a bunch of sections of the XML document that repeats. I thought I\'d use multiple string templates as base
As far as I know, you can't do it with SimpleXML because addChild doesn't make a deep copy of the element (being necessary to specify the tag name can easily be overcome by calling SimpleXMLElement::getName()).
One solution would be to use DOM instead:
With this function:
function sxml_append(SimpleXMLElement $to, SimpleXMLElement $from) {
$toDom = dom_import_simplexml($to);
$fromDom = dom_import_simplexml($from);
$toDom->appendChild($toDom->ownerDocument->importNode($fromDom, true));
}
We have for
<?php
header("Content-type: text/plain");
$sxml = simplexml_load_string("<root></root>");
$n1 = simplexml_load_string("<child>one</child>");
$n2 = simplexml_load_string("<child><k>two</k></child>");
sxml_append($sxml, $n1);
sxml_append($sxml, $n2);
echo $sxml->asXML();
the output
<?xml version="1.0"?>
<root><child>one</child><child><k>two</k></child></root>
See also some user comments that use recursive functions and addChild, e.g. this one.
You could use this function that is based in creating the children with attributes from the source:
function xml_adopt($root, $new) {
$node = $root->addChild($new->getName(), (string) $new);
foreach($new->attributes() as $attr => $value) {
$node->addAttribute($attr, $value);
}
foreach($new->children() as $ch) {
xml_adopt($node, $ch);
}
}
$xml = new SimpleXMLElement("<root/>");
$child = new SimpleXMLElement("<content><p a=\"aaaaaaa\">a paragraph</p><p>another <br/>p</p></content>");
xml_adopt($xml, $child);
echo $xml->asXML()."\n";
This will produce:
<?xml version="1.0"?>
<root><content><p a="aaaaaaa">a paragraph</p><p>another p<br/></p></content></root>
The xml_adopt() example doesn't preserve namespace nodes.
My edit was rejected because it changed to much? was spam?.
Here is a version of xml_adopt() that preserves namespaces.
function xml_adopt($root, $new, $namespace = null) {
// first add the new node
// NOTE: addChild does NOT escape "&" ampersands in (string)$new !!!
// replace them or use htmlspecialchars(). see addchild docs comments.
$node = $root->addChild($new->getName(), (string) $new, $namespace);
// add any attributes for the new node
foreach($new->attributes() as $attr => $value) {
$node->addAttribute($attr, $value);
}
// get all namespaces, include a blank one
$namespaces = array_merge(array(null), $new->getNameSpaces(true));
// add any child nodes, including optional namespace
foreach($namespaces as $space) {
foreach ($new->children($space) as $child) {
xml_adopt($node, $child, $space);
}
}
}
(edit: example added)
$xml = new SimpleXMLElement(
'<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:media="http://search.yahoo.com/mrss/">
<channel></channel></rss>');
$item = new SimpleXMLElement(
'<item xmlns:media="http://search.yahoo.com/mrss/">
<title>Slide Title</title>
<description>Some description</description>
<link>http://example.com/img/image.jpg</link>
<guid isPermaLink="false">A1234</guid>
<media:content url="http://example.com/img/image.jpg" medium="image" duration="15">
</media:content>
</item>');
$channel = $xml->channel;
xml_adopt($channel, $item);
// output:
// Note that the namespace is (correctly) only preserved on the root element
'<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
<channel>
<item>
<title>Slide Title</title>
<description>Some description</description>
<link>http://example.com/img/image.jpg</link>
<guid isPermaLink="false">A1234</guid>
<media:content url="http://example.com/img/image.jpg" medium="image" duration="15">
</media:content>
</item>
</channel>
</rss>'