Search code examples
phpxmldomserversimplexml

How to add existing elements from XML file B to XML file A


I have 2 XML files.

I am trying to get data from XML File B and add it to XML File A. I want to first add an empty child to XML File A, and then in that child, I want to add data from XML File B (in this case, the animal child element). I want to achieve this through either SimpleXML or DOMDocument. I have been stuck on this. Any help would be great!

personinfo.xml < XML File A

<personinfo>
    <personN number="1">
        <personL letter="A">
            <fullname>
                <firstname>Summer</firstname>
                <lastname>Smith</lastname>
            </fullname>
            <favourites>
                <color>pink</color>
            </favourites>
        </personL>  
    </personN>

    <personN number="2">
        <personL letter="B">
            <fullname>
                <firstname>Autumn</firstname>
                <lastname>Smith</lastname>
            </fullname>
            <favourites>
                <color>blue</color>
            </favourites>
        </personL>  
    </personN>
</personinfo>

favouritesinfo.xml < XML File B

<favouritesinfo>
    <personN number="1">
            <animal>cat</animal>
    </personN>
    <personN number="2">
            <animal>dog</animal>
    </personN>
</favouritesinfo>

Is this possible? If so, am I able to do this via XML DOM Document or SimpleXML?

I attempted doing it this way via a separate PHP file but it does not work/do anything with SimpleXML

$personXML = simplexml_load_file("personinfo.xml");
$faveXML = simplexml_load_file("favouritesinfo.xml");

$animal=$faveXML->personN[0]->animal;

$newFave=$personXML->personN->personL[0]->addChild("favourites");
$favourites->addChild($animal);

$personXML->asXML();

Desired output

<personinfo>
    <personN number="1">
        <personL letter="A">
            <fullname>
                <firstname>Summer</firstname>
                <lastname>Smith</lastname>
            </fullname>
            <favourites>
                <color>pink</color>
            </favourites>
            <favourites>
                <color>cat</color>
            </favourites>
        </personL>  
    </personN>

    <personN number="2">
        <personL letter="B">
            <fullname>
                <firstname>Autumn</firstname>
                <lastname>Smith</lastname>
            </fullname>
            <favourites>
                <color>blue</color>
            </favourites>
            <favourites>
                <color>dog</color>
            </favourites>
        </personL>  
    </personN>
</personinfo>

Solution

  • This is easier in DOM because you have access to the nodes and can copy them from one document to another. Use Xpath to fetch parts of a document.

    // load the XML into DOM document and create Xpath instances
    $personsDocument = new DOMDocument;
    $personsDocument->preserveWhiteSpace = FALSE;
    $personsDocument->loadXML($xmlPersons);
    $personsXpath = new DOMXpath($personsDocument);
    
    $favoritesDocument = new DOMDocument;
    $favoritesDocument->preserveWhiteSpace = FALSE;
    $favoritesDocument->loadXML($xmlFavorites);
    $favoritesXpath = new DOMXpath($favoritesDocument);
    
    // iterate the personN nodes in the favorties XML
    foreach ($favoritesXpath->evaluate('.//personN') as $personFavorites) {
        $personId = (int)$personFavorites->getAttribute('number');
        // find the matching personL node in the persons XML
        foreach ($personsXpath->evaluate('.//personN[@number='.$personId.']/personL') as $personNode) {
            // check if it has a 'favourites' child already
            $favoritesNode = $personsXpath->evaluate('favourites', $personNode)->item(0);
            if (!$favoritesNode) {
                // otherwise create one
                $favoritesNode = $personNode->appendChild(
                    $personsDocument->createElement('favourites')
                );
            }
            // now iterate the different favorites of a person
            foreach ($personFavorites->childNodes as $favorite) {
                // import and add
                $favoritesNode->appendChild($personsDocument->importNode($favorite, TRUE));
            }
        }
    }
    
    $personsDocument->formatOutput = TRUE;
    echo $personsDocument->saveXML();
    

    Output:

    <?xml version="1.0"?>
    <personinfo>
      <personN number="1">
        <personL letter="A">
          <fullname>
            <firstname>Summer</firstname>
            <lastname>Smith</lastname>
          </fullname>
          <favourites>
            <color>pink</color>
            <animal>cat</animal>
          </favourites>
        </personL>
      </personN>
      <personN number="2">
        <personL letter="B">
          <fullname>
            <firstname>Autumn</firstname>
            <lastname>Smith</lastname>
          </fullname>
          <favourites>
            <color>blue</color>
            <animal>dog</animal>
          </favourites>
        </personL>
      </personN>
    </personinfo>
    

    Adding a favourites parent for each favorite does not make much sense. So I added a check to my example. If you really want the parent nodes, you can remove it:

    foreach ($personsXpath->evaluate('.//personN[@number='.$personId.']/personL') as $personNode) {
        //create 'favourites' child
        $favoritesNode = $personNode->appendChild(
            $personsDocument->createElement('favourites')
        );
        foreach ($personFavorites->childNodes as $favorite) {
        //...