0

I'm trying to combine multiple XML files that have the same structure into one file.

This is the structure of my XML files:

file1.xml:

<root information="file1">
 <items>
  <item>FOO</item>
  <item>BAR</item>
 </items>
</root>

file2.xml:

<root information="file2">
 <items>
  <item>BAR</item>
  <item>FOO</item>
 </items>
</root>

Using this code I've been able to combine them:

$files= array(
  'file1.xml',
  'file2.xml'
);

$dom = new DOMDocument();
$dom->appendChild($dom->createElement('root'));

foreach ($files as $filename) {
  $addDom = new DOMDocument();

  $addDom->load($filename);
  if ($addDom->documentElement) {
    foreach ($addDom->documentElement->childNodes as $node) {
      $dom->documentElement->appendChild(
        $dom->importNode($node, TRUE)
      );

    }

  }
}

$dom->save('output.xml');

This works partly but removes the original root element which has the information attribute that I still need. So I would like to keep all the existing root elements but wrap them in a new root element. This is what I would like to end up with:

<files>
 <root information="file1">
  <items>
   <item>FOO</item>
   <item>BAR</item>
  </items>
 </root>
 <root information="file2">
  <items>
   <item>BAR</item>
   <item>FOO</item>
  </items>
 </root>
</files>

But I can't figure out how to append the file. Whatever I try it only ends up at the bottom of the output file instead of appending all old root elements. I'm guessing this is simple as hell but I just can't figure it out. Help much appreciated!

2
  • Why use DOMDocument and not just append them straight into a new file? Commented Jun 7, 2017 at 14:41
  • Why bother with XML processing? Just echo your start tag, dump the files, echo your end tag. Commented Jun 7, 2017 at 14:41

2 Answers 2

1

Actually your source only has some minor mistakes. You create a root document element in the target document, not the files element in you example. Additionally your copy of the nodes in the source documents is a level to deep, you just need to import their document elements.

I modified your code a little to make it self contained and fixed the mistakes.

$files= array(
  'file1.xml' => 
'<root information="file1">
 <items>
  <item>FOO</item>
  <item>BAR</item>
 </items>
</root>',
  'file2.xml' => 
'<root information="file2">
 <items>
  <item>BAR</item>
  <item>FOO</item>
 </items>
</root>'
);

// create a target document with a files root
$target = new DOMDocument();
$target->appendChild($target->createElement('files'));

// iterate the source files array
foreach ($files as $name => $content) {
  // load each source
  $source = new DOMDocument();
  $source->loadXml($content);
  // if it has a document element
  if ($source->documentElement) {
    // copy it to the target document
    $target->documentElement->appendChild(
      $target->importNode($source->documentElement, TRUE)
    );
  }
}

$target->formatOutput = TRUE;
echo $target->saveXml();
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you! Got this working like a charm. Had to use load instead of loadXml, for some reason it wouldn't work otherwise. Also had to specify the charset after new DOMDocument since my source files are in some hideous windows-1252 format that is beyond my control.
1

Consider XSLT the special-purpose language designed to transform XML files. XSLT maintains the document() function to parse from external files at paths relative to script. PHP can run XSLT 1.0 scripts with its php-xsl class. Be sure to enable this extension in .ini file.

Should your files be very numerous such as hundreds, consider building the XSLT script on the fly in PHP loop. As information, XSLT is a well-formed XML file, so can be parsed from file or string.

XSLT (save as .xsl file in same directory as all XML files)

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="/">
        <files>
            <xsl:copy-of select="root"/>
            <xsl:copy-of select="document('file2.xml')/root"/>
            <xsl:copy-of select="document('file3.xml')/root"/>
            <xsl:copy-of select="document('file4.xml')/root"/>                 
            <!-- add more as needed -->
        </files>
    </xsl:template>

</xsl:stylesheet>

PHP (load first XML and XSL scripts, then transform/output)

// LOAD XML SOURCE
$xml = new DOMDocument('1.0', 'UTF-8');
$xml->load('file1.xml');                  // ONLY LOAD FIRST XML

// LOAD XSL SOURCE
$xsl = new DOMDocument('1.0', 'UTF-8');
$xsl->load('XSLT_Script.xsl');

// TRANSFORM XML
$proc = new XSLTProcessor;
$proc->importStyleSheet($xsl);
$newXML = $proc->transformToXML($xml);

// SAVE NEW XML
file_put_contents('Output.xml', $newXML);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.