2

I looked in several question and documents and parts of code around but do not have a good idea what I'm doing wrong.

I need to combine 8 different XML's into one XML which eventually will be 1 html file. The filenames of the XML are dynamically generated. In the example I use only 2 xml files.

XML1 in variabele : $file_xml_cv

<?xml version="1.0" encoding="UTF-8"?>
    <node>
      <cv>
        <cvid>584</cvid>
        <titel>Dit is een test cv</titel>
        <naam>DHR H.V.H. Dagobert Duck</naam>
      </cv>
    </node>

XML2 in variabele $file_xml_werkgevers

<?xml version="1.0" encoding="UTF-8"?>
    <node>
      <werkgevers>
        <naam>Company 1</naam>
        <Functie>Projectmanager en Informatie analist</Functie>
        <periode>1967-01-01 00:00:00</periode>
        <einddatum>1967-01-01 00:00:00</einddatum>
      </werkgevers>
      <werkgevers>
        <naam>Company 2</naam>
        <Functie>Systeemontwerper</Functie>
        <periode/>
        <einddatum/>
      </werkgevers>
    </node>

Which should result in XML3

<?xml version="1.0" encoding="UTF-8"?>
<node>
    <cv>
       <cvid>584</cvid>
       <titel>Dit is een test cv</titel>
       <naam>DHR H.V.H. Dagobert Duck</naam>
    </cv>
    <werkgevers>
       <naam>Company 1</naam>
       <Functie>Projectmanager en Informatie analist</Functie>
       <periode>1967-01-01 00:00:00</periode>
       <einddatum>1967-01-01 00:00:00</einddatum>
     </werkgevers>
     <werkgevers>
        <naam>Company 2</naam>
        <Functie>Systeemontwerper</Functie>
        <periode/>
        <einddatum/>
     </werkgevers>
</node>

In order to do translation to html with XSLT i have:

    <?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:m="http://www.example.com/"
    exclude-result-prefixes="xs"
    version="2.0">

<xsl:output method="html" version="4.0" encoding="UTF-8" indent="yes"/>

<xsl:template match="/">
  <html>
  <body>

 <h2><xsl:value-of select="node/cv/naam"/></h2>

  <xsl:if test="node/werkgevers">
  <table border="1">
     <tr bgcolor="#9acd32">
      <th>Werkgevers</th>
      <th></th>
      <th></th>
      <th></th>
    </tr>

    <xsl:for-each select="node/werkgevers">
    <tr>
      <td>>></td>
      <td><xsl:value-of select="periode"/></td>
      <td><xsl:value-of select="naam"/></td>
      <td><xsl:value-of select="functie"/></td>
    </tr>
    </xsl:for-each>
  </table>
  </xsl:if>
  </body>
  </html>
</xsl:template>

</xsl:stylesheet>

Currently I combine the first and second XML with string manipulations which is not a very good solution I think. I tried several php code but could not solve it.

Currently I merge now 1 xml with 1 xslt with the following code which is working good.

  // Load the XML source
  $xml_cv = new DOMDocument;
  $xml_cv->load($file_xml_total);

  // Load the XLS
  $xsl_cv = new DOMDocument;
  $xsl_cv->load($file_xsl_cv);

  // Configure the transformer
  $proc = new XSLTProcessor;
  $proc->importStyleSheet($xsl_cv); // attach the xsl rules
  $proc->transformToURI($xml_cv, $file_html_cv);

So the question is:

1) How to combine more XML files into 1 XML file so I can use the same PHP code as mentioned?

or

2) Another solution could be to make use of XMLT 2.0 document function to combine different XML trough 1 xslt but I could not find any solution how to deal with this as the names of the files are dynamic. One of the answers of this is: XSLT: Merging two log files with different structure and time-representation

But in here there are fixed names as mentioned in:

<!-- The source-documents. -->
<xsl:variable name="doc1" select="doc('log1.xml')"/>
<xsl:variable name="doc2" select="doc('log2.xml')"/>

How to solve this second approach?


Development based on new Input

I'm a little bit lost... Based on the new input I looked into it and I had troubles with getting the same results. After a few hours it looked that the processing

$proc->transformToXML

is not working so I went on to make it ever more simpel. So now after many hours i have the following very simple approach:

The code:

  $file_xsl_merge = 'C:\www\arlande.nl\sites\default\files\node_export\merge.xsl';
  $xsl = new DOMDocument('1.0', 'UTF-8');
  $xsl->load($file_xsl_merge);
  writetolog ("Dump of xslt: ". $xsl->savexml());

  $initXML = '<?xml version="1.0" encoding="UTF-8"?><node><dummy>33</dummy></node>';
  $xml = new DOMDocument('1.0', 'UTF-8');
  $xml->LoadXML ($initXML);
  writetolog ("Dump of XML: ". $xml->savexml());

  // TRANSFORM XML
  $proc = new XSLTProcessor;
  $proc->importStyleSheet($xsl);
  $newXML = $proc->transformToXML($xml,$file_xml_total);
  writetolog ("A dump of xml after processing is: ". $xml->savexml());
  writetolog ("String newXML is : ". $newXML);
  writetolog( "All XML should be combine now in file : " . $file_xml_total);

The merge.xls:

<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="*"/>

     <!-- The source-documents. -->
    <xsl:variable name="doc1" select="document('C:\www\arlande.nl\sites\default\files\node_export\cv.xml')"/>
    <xsl:variable name="doc2" select="document('C:\www\arlande.nl\sites\default\files\node_export\werkgevers.xml')"/>
    <xsl:variable name="doc3" select="document('C:\www\arlande.nl\sites\default\files\node_export\opleiding.xml')"/>
    <xsl:variable name="doc4" select="document('C:\www\arlande.nl\sites\default\files\node_export\opdracht_b.xml')"/>
    <xsl:variable name="doc5" select="document('C:\www\arlande.nl\sites\default\files\node_export\opdracht_s.xml')"/>

    <xsl:template match="node">
        <xsl:copy>
            <xsl:copy-of select="*"/>
            <xsl:copy-of select="$doc1/node/*"/>
            <xsl:copy-of select="$doc2/node/*"/>
            <xsl:copy-of select="$doc3/node/*"/>
            <xsl:copy-of select="$doc4/node/*"/>
            <xsl:copy-of select="$doc5/node/*"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

I created a log file from this code and that is displaying:

16:22:00 Start run
16:22:00 s:1186:"Dump of xslt: <?xml version="1.0"?>
<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="*"/>

     <!-- The source-documents. -->
    <xsl:variable name="doc1" select="document('C:\www\arlande.nl\sites\default\files\node_export\cv.xml')"/>
    <xsl:variable name="doc2" select="document('C:\www\arlande.nl\sites\default\files\node_export\werkgevers.xml')"/>
    <xsl:variable name="doc3" select="document('C:\www\arlande.nl\sites\default\files\node_export\opleiding.xml')"/>
    <xsl:variable name="doc4" select="document('C:\www\arlande.nl\sites\default\files\node_export\opdracht_b.xml')"/>
    <xsl:variable name="doc5" select="document('C:\www\arlande.nl\sites\default\files\node_export\opdracht_s.xml')"/>

    <xsl:template match="node">
        <xsl:copy>
            <xsl:copy-of select="*"/>
            <xsl:copy-of select="$doc1/node/*"/>
            <xsl:copy-of select="$doc2/node/*"/>
            <xsl:copy-of select="$doc3/node/*"/>
            <xsl:copy-of select="$doc4/node/*"/>
            <xsl:copy-of select="$doc5/node/*"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>
";
16:22:00 s:83:"Dump of XML: <?xml version="1.0" encoding="UTF-8"?>
<node><dummy>33</dummy></node>
";
16:22:00 s:105:"A dump of xml after processing is: <?xml version="1.0" encoding="UTF-8"?>
<node><dummy>33</dummy></node>
";
16:22:00 s:19:"String newXML is : ";
16:22:00 s:108:"All XML should be combine now in file : C:/www/arlande.nl/sites/default/files/node_export/node_584_total.xml";

So again the processing of the document function in xslt is not working...

  1. I tried several combinaties in the search match /node.
  2. All seperate XML's files are there and all starting with ...

Any suggestion how to get this working?

2 Answers 2

1

Variant 1: Use XPath 1.0 document()

Actually XSLT 1.0 supports document() to load additional XML documents. (<xsl:variable name="doc1" select="document('log1.xml')"/>) The name can be semi-dynamic. You can use an expression on the main XML to calculate it. But it won't work with values from a loop.

Variant 2: Merge XMLs in PHP

To merge XML documents in PHP you would need to define an outer framework and copy the nodes.

$xmls = [
  'one.xml' => '<one/>',
  'three.xml' => '<two/>',
  'two.xml' => '<three/>'
];

$target = new DOMDocument();
$target->appendChild($target->createElement('files'));
foreach ($xmls as $fileName => $content) {
  $fileNode = $target
     ->documentElement
     ->appendChild($target->createElement('file'));
  $fileNode->setAttribute('name', $fileName);

  $source= new DOMDocument();
  $source->loadXml($content);
  $fileNode->appendChild(
    $target->importNode($source->documentElement, TRUE)
 );
} 

$target->formatOutput = TRUE;
echo $target->saveXml();

Output:

<?xml version="1.0"?>
<files>
  <file name="one.xml">
    <one/>
  </file>
  <file name="three.xml">
    <two/>
  </file>
  <file name="two.xml">
    <three/>
  </file>
</files>

The example uses strings to make it self contained. Adapting it to files would mean using $source->load($fileName);.

Variant 3: Use a callback into PHP to load documents

Last PHP allows to use callbacks from XSLT. They need to be registered using XsltProcessor::registerPHPFunctions() Together with eXSLT you can define a function that can be used in a loop.

<?xml version="1.0"?>
<xsl:stylesheet
  version="1.0"
  xmlns="http://www.w3.org/1999/xhtml/"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:php="http://php.net/xsl"
  xmlns:func="http://exslt.org/functions"
  xmlns:cxr="urn:your-function-namespace"
  extension-element-prefixes="php func"
  exclude-result-prefixes="#default cxr">

<func:function name="cxr:load-document">
  <xsl:param name="url"/>
  <func:result select="php:function('\YourNameSpace\XsltCallback::loadDocument', $url)"/>
</func:function>

</xsl:stylesheet>

And the PHP function:

namespace \YourNameSpace\XsltCallback {

    public function loadDocument($fileName) {
      $dom = new \DOMDocument();
      $dom->load($fileName);
      return $dom;
    }
}
Sign up to request clarification or add additional context in comments.

5 Comments

Many thxs. I did not now the callback from PHP with XSLT. I will look into this...I understand the first code and I like it. Is there a way to copy elements and corresponding attributes in a general way so I just generated a complete new xml easily?
I added some titles. The second variant just merges XMLs. The main problem with that is memory consumption. Using large XML files has limits because you need to load all of it at once. The callback on the other hand is controlled from XSLT and can load the separate XML documents. If you like to see a project using it check github.com/ThomasWeinert/Xslrunner
I'm do not get variant 1 working. See additional info I added at the question. Variant 2 I do not like as I need to enter all element names in the code.. How to get variant 1 working?
The function name is document() not doc(). What's the reported error?
I change it to document( but no result. The XSLT transformation gives no error... i updated the xlst and log in the question with this run..
0

Consider using a temp.xml file with XSLT. When combining multiple XML files, you need to read in the first XML then use document() on the next.

$file_xml_cv = '<?xml version="1.0" encoding="UTF-8"?>
                <node>
                  <cv>
                    <cvid>584</cvid>
                    <titel>Dit is een test cv</titel>
                    <naam>DHR H.V.H. Dagobert Duck</naam>
                  </cv>
                </node>';

$file_xml_werkgevers = '<?xml version="1.0" encoding="UTF-8"?>
                        <node>
                          <werkgevers>
                            <naam>Company 1</naam>
                            <Functie>Projectmanager en Informatie analist</Functie>
                            <periode>1967-01-01 00:00:00</periode>
                            <einddatum>1967-01-01 00:00:00</einddatum>
                          </werkgevers>
                          <werkgevers>
                            <naam>Company 2</naam>
                            <Functie>Systeemontwerper</Functie>
                            <periode/>
                            <einddatum/>
                          </werkgevers>
                        </node>';

// LOAD XML SOURCE
$xml = new DOMDocument('1.0', 'UTF-8');
$xml->loadXML($file_xml_cv);

/// LOAD XSLT 
$xsl = new DOMDocument('1.0', 'UTF-8');
file_put_contents($cd.'/temp.xml', $file_xml_werkgevers);   // OUTPUT TEMP FILE

$xslstr ='<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="node">
                <xsl:copy>
                    <xsl:copy-of select="*"/>
                    <xsl:copy-of select="document(\'temp.xml\')/node/*"/>
                </xsl:copy>
            </xsl:template>

           </xsl:stylesheet>';
$xsl->loadXML($xslstr);

// TRANSFORM XML
$proc = new XSLTProcessor;
$proc->importStyleSheet($xsl);
$newXML = $proc->transformToXML($xml);
unlink ($cd.'/temp.xml');                                 // DELETE TEMP FILE

echo $newXML;

# <?xml version="1.0" encoding="UTF-8"?>
# <node>
#   <cv>
#     <cvid>584</cvid>
#     <titel>Dit is een test cv</titel>
#     <naam>DHR H.V.H. Dagobert Duck</naam>
#   </cv>
#   <werkgevers>
#     <naam>Company 1</naam>
#     <Functie>Projectmanager en Informatie analist</Functie>
#     <periode>1967-01-01 00:00:00</periode>
#     <einddatum>1967-01-01 00:00:00</einddatum>
#   </werkgevers>
#   <werkgevers>
#     <naam>Company 2</naam>
#     <Functie>Systeemontwerper</Functie>
#     <periode/>
#     <einddatum/>
#   </werkgevers>
# </node>

You can even run the transformation in a loop for all 8 XML files. Below iterates with $file_xml_werkgevers three times. Use end result for your html transformation.

// LOAD XML SOURCE
$xml = new DOMDocument('1.0', 'UTF-8');
$xml->loadXML($file_xml_cv);

foreach(array($file_xml_werkgevers, $file_xml_werkgevers, $file_xml_werkgevers) as $temp){
    $xsl = new DOMDocument('1.0', 'UTF-8');
    file_put_contents($cd.'/temp.xml', $temp);   // OUTPUT TO TEMP FILE

    $xslstr ='<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="node">
                    <xsl:copy>
                        <xsl:copy-of select="*"/>
                        <xsl:copy-of select="document(\'temp.xml\')/node/*"/>
                    </xsl:copy>
                </xsl:template>

               </xsl:stylesheet>';
    $xsl->loadXML($xslstr);

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

    unlink ($cd.'/temp.xml');                                 // DELETE TEMP FILE
}

echo $newXML;

# <?xml version="1.0" encoding="UTF-8"?>
# <node>
#   <cv>
#     <cvid>584</cvid>
#     <titel>Dit is een test cv</titel>
#     <naam>DHR H.V.H. Dagobert Duck</naam>
#   </cv>
#   <werkgevers>
#     <naam>Company 1</naam>
#     <Functie>Projectmanager en Informatie analist</Functie>
#     <periode>1967-01-01 00:00:00</periode>
#     <einddatum>1967-01-01 00:00:00</einddatum>
#   </werkgevers>
#   <werkgevers>
#     <naam>Company 2</naam>
#     <Functie>Systeemontwerper</Functie>
#     <periode/>
#     <einddatum/>
#   </werkgevers>
#   <werkgevers>
#     <naam>Company 1</naam>
#     <Functie>Projectmanager en Informatie analist</Functie>
#     <periode>1967-01-01 00:00:00</periode>
#     <einddatum>1967-01-01 00:00:00</einddatum>
#   </werkgevers>
#   <werkgevers>
#     <naam>Company 2</naam>
#     <Functie>Systeemontwerper</Functie>
#     <periode/>
#     <einddatum/>
#   </werkgevers>
#   <werkgevers>
#     <naam>Company 1</naam>
#     <Functie>Projectmanager en Informatie analist</Functie>
#     <periode>1967-01-01 00:00:00</periode>
#     <einddatum>1967-01-01 00:00:00</einddatum>
#   </werkgevers>
#   <werkgevers>
#     <naam>Company 2</naam>
#     <Functie>Systeemontwerper</Functie>
#     <periode/>
#     <einddatum/>
#   </werkgevers>
# </node>

8 Comments

Many many thxs. I will definitly implement the loop and do some testing with it. I will let you now the result ! Many thxs!
Parfait? I spend many hours to get it work as I liked the code but without any result. I get it work for 1 xml but not merging. I made a more easy approach as added in my question. Any suggestion what I can do?
You are writing to log for after processing and to final file the same line which is original document: $xml->saveXML(). The transformation output would be the string variable, $newXML, which seems cut off from log. Dump this string (not DOM object) to file. By the way first xpaths using node* should look to child elements as node/*.
The output to file is indeed not correct but unfortunality the output of $newXML is empty. It is not cut off the log :-( Regarding the syntax of xpaths.. so the syntax with doc3 : select="$doc3/node/*"/> is correct?
Do you receive any error? Check error logs. Paths for $doc1 and $doc2 need to be adjusted. I also should mention all external XML documents should be in same directory as the XSLT file or if using subfolder include it in document: document('/subfolder/doc.XML').
|

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.