SimpleXML: append one tree to another

I have two XML trees and would like to add one tree as a leaf to the other one.

Apparently:

$tree2->addChild('leaf', $tree1);

doesn’t work, as it copies only the first root node.

Ok, so then I thought I would traverse the whole first tree, adding every element one by one to the second one.

But consider XML like this:

<root>
  aaa
  <bbb/>
  ccc
</root>

How do I access “ccc”? tree1->children() returns just “bbb”… .

Here is Solutions:

We have many solutions to this problem, But we recommend you to use the first solution because it is tested & true solution that will 100% work for you.

Solution 1

You can’t add a “tree” directly using SimpleXML, as you have seen. However, you can use some DOM methods to do the heavy lifting for you whilst still working on the same underlying XML.

$xmldict = new SimpleXMLElement('<dictionary><a/><b/><c/></dictionary>');
$kitty   = new SimpleXMLElement('<cat><sound>meow</sound><texture>fuzzy</texture></cat>');

// Create new DOMElements from the two SimpleXMLElements
$domdict = dom_import_simplexml($xmldict->c);
$domcat  = dom_import_simplexml($kitty);

// Import the <cat> into the dictionary document
$domcat  = $domdict->ownerDocument->importNode($domcat, TRUE);

// Append the <cat> to <c> in the dictionary
$domdict->appendChild($domcat);

// We can still use SimpleXML! (meow)
echo $xmldict->c->cat->sound;

Solution 2

You can use this class to SimpleXML objects that accept children append

<?php

class MySimpleXMLElement extends SimpleXMLElement
{
    /**
     * Add SimpleXMLElement code into a SimpleXMLElement
     *
     * @param MySimpleXMLElement $append
     */
    public function appendXML($append)
    {
        if ($append) {
            if (strlen(trim((string)$append)) == 0) {
                $xml = $this->addChild($append->getName());
            } else {
                $xml = $this->addChild($append->getName(), (string)$append);
            }

            foreach ($append->children() as $child) {
                $xml->appendXML($child);
            }

            foreach ($append->attributes() as $n => $v) {
                $xml->addAttribute($n, $v);
            }
        }
    }
}

Solution 3

This is a nice solution from comment on PHP manual page (using only SimpleXML, not DOM):

function append_simplexml(&$simplexml_to, &$simplexml_from)
{
    foreach ($simplexml_from->children() as $simplexml_child)
    {
        $simplexml_temp = $simplexml_to->addChild($simplexml_child->getName(), (string) $simplexml_child);
        foreach ($simplexml_child->attributes() as $attr_key => $attr_value)
        {
            $simplexml_temp->addAttribute($attr_key, $attr_value);
        }
       
        append_simplexml($simplexml_temp, $simplexml_child);
    }
}

Solution 4

Very nice Theo Heikonnen
Slight tweaking to make it work the way I wanted

    function addsubtree(&$xml1,&$xml2)
    {// Create new DOMElements from the two SimpleXMLElements
        $dom1 = dom_import_simplexml($xml1);
        $dom2 = dom_import_simplexml($xml2);
        // Import the  into the  document
        $dom2 = $dom1->ownerDocument->importNode($dom2, TRUE);
        // Append the  to 
        $dom1->appendChild($dom2);
    }

    $xml1 = new SimpleXMLElement('<xml/>');
    $xml2 = new SimpleXMLElement('<sub/>');

    $xml2->addChild('test','data');
    $temp=$xml1->addChild('sub1');

    header('Content-type: text/xml');
    header('Pragma: public');
    header('Cache-control: private');
    header('Expires: -1');
    addsubtree($temp,$xml2);

    // We can still use SimpleXML! (meow)
    echo $xml1->asXML();

Note: Use and implement solution 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply