Jump to content
Billy-3k

Merge XML files via PHP

Recommended Posts

Hello,

I've created a PHP file to merge XML files by ReferenceID(product_print_id)  but i don't get the result that i want. I think i must use cloneNode and DOMNode::insertBefore but I think i'm lost.

The first file is prodInfo.xml:

<?xml version="1.0" encoding="utf-8"?>
<PRODUCTINFORMATION>
<PRODUCTS>
    <PRODUCT>
        <PRODUCT_NUMBER>53-03</PRODUCT_NUMBER>
        <PRODUCT_PRINT_ID>42</PRODUCT_PRINT_ID>
        <PRODUCT_NAME>ProductFirst</PRODUCT_NAME>
        <COLOR_CODE>03</COLOR_CODE>
    </PRODUCT>
</PRODUCTS>
</PRODUCTINFORMATION>

and the second file is printInfo.xml:

<?xml version="1.0" encoding="utf-8"?>
<PRINTINGINFORMATION>
  <PRODUCTS>
    <PRODUCT>
      <PRODUCT_PRINT_ID>42</PRODUCT_PRINT_ID>
      <PRINTING_POSITIONS>
        <PRINTING_POSITION>
          <ID>TOP BOX</ID>
          <PRINTING_TECHNIQUE>
            <ID>DL</ID>
          </PRINTING_TECHNIQUE>
          <PRINTING_TECHNIQUE>
            <ID>L2</ID>
          </PRINTING_TECHNIQUE>
          <PRINTING_TECHNIQUE>
            <ID>P4</ID>
          </PRINTING_TECHNIQUE>
        </PRINTING_POSITION>
      </PRINTING_POSITIONS>
    </PRODUCT>
  </PRODUCTS>
</PRINTINGINFORMATION>

The php file i created is the following:

<?php
header ("Content-Type:text/xml");
$target = new DOMDocument();
$target->preserveWhiteSpace = FALSE;
$target->load('prodInfo.xml');
$targetXpath = new DOMXpath($target);


$source = new DOMDocument();
$source->load('printInfo.xml');
$sourceXpath = new DOMXpath($source);
foreach ($targetXpath->evaluate('//PRODUCT') as $PRODUCTNode) {
  $PRODUCT_PRINT_ID = $targetXpath->evaluate('string(PRODUCT_PRINT_ID)', $PRODUCTNode);
  foreach ($sourceXpath->evaluate('//PRODUCT[PRODUCT_PRINT_ID="'.$PRODUCT_PRINT_ID.'"]/*[not(self::PRODUCT_PRINT_ID)]') as $node) {
    $PRODUCTNode->appendChild(
      $target->importNode($node, TRUE)
    );
  }
}


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

The output/result i get is this:

<?xml version="1.0" encoding="utf-8"?>
<PRODUCTINFORMATION>
	<PRODUCTS>
		<PRODUCT>
			<PRODUCT_NUMBER>53-03</PRODUCT_NUMBER>
			<PRODUCT_PRINT_ID>42</PRODUCT_PRINT_ID>
			<PRODUCT_NAME>ProductFirst</PRODUCT_NAME>
			<COLOR_CODE>03</COLOR_CODE>
			<PRINTING_POSITIONS>
				<PRINTING_POSITION>
					<ID>TOP BOX</ID>
					<PRINTING_TECHNIQUE>
						<ID>DL</ID>
					</PRINTING_TECHNIQUE>
					<PRINTING_TECHNIQUE>
						<ID>L2</ID>
					</PRINTING_TECHNIQUE>
					<PRINTING_TECHNIQUE>
						<ID>P4</ID>
					</PRINTING_TECHNIQUE>
				</PRINTING_POSITION>
			</PRINTING_POSITIONS>
		</PRODUCT>
	</PRODUCTS>
</PRODUCTINFORMATION>

But what i want to achieve is this:

<?xml version="1.0" encoding="utf-8"?>
<PRODUCTINFORMATION>
	<PRODUCTS>
		<PRODUCT>
			<PRODUCT_NUMBER>53-03</PRODUCT_NUMBER>
			<PRODUCT_PRINT_ID>42</PRODUCT_PRINT_ID>
			<PRODUCT_NAME>ProductFirst</PRODUCT_NAME>
			<PRINTING_POSITIONS>
				<PRINTING_POSITION>
					<ID>TOP BOX</ID>
					<PRINTING_TECHNIQUE>
						<ID>DL</ID>
					</PRINTING_TECHNIQUE>
					<COLOR_CODE>03</COLOR_CODE>
				</PRINTING_POSITION>
				<PRINTING_POSITION>
					<ID>TOP BOX</ID>
					<PRINTING_TECHNIQUE>
						<ID>L2</ID>
					</PRINTING_TECHNIQUE>
					<COLOR_CODE>03</COLOR_CODE>
				</PRINTING_POSITION>
				<PRINTING_POSITION>
					<ID>TOP BOX</ID>
					<PRINTING_TECHNIQUE>
						<ID>P4</ID>
					</PRINTING_TECHNIQUE>
					<COLOR_CODE>03</COLOR_CODE>
				</PRINTING_POSITION>
			</PRINTING_POSITIONS>
		</PRODUCT>
	</PRODUCTS>
</PRODUCTINFORMATION>

So as you can see i want to repeat the field PRINTING POSITION ID for every PRINTING TECHNIQUE and also i want to repeat the field COLOR CODE for every PRINTING POSITION.

Anyone can help me with this?

Thanks in advance.

Share this post


Link to post
Share on other sites

It's quite happily copying what you've told it to copy. Without any changes.

Thing is you don't want to just copy the nodes. What you want is to change the structure of the XML. Which means you have to write code that understands the original structures and the structures you're trying to create.

1. Copy the PRODUCT_NUMBER, PRODUCT_PRINT_ID, PRODUCT_NAME
2. Create a new PRINTING_POSITIONS to work on. For each PRINTING_POSITION,
3. Create a new PRINTING_POSITION to work on. For each PRINTING_TECHNIQUE,
4. Copy the ID
5. Copy the PRINTING_TECHNIQUE
6. Either (a) copy the COLOR_CODE or (b) copy everything except the PRODUCT_NUMBER, PRINT_ID, and NAME

  • Like 1

Share this post


Link to post
Share on other sites

Thank you for your reply!

I guess i must use cloneNode to clone COLOR_CODE and insertBefore to place it where i want, right? Or is there an easier way to achieve that?

It sounds a lot of work and kinda hard for me and i'm wondering if it's better to start over with XSLT.

Share this post


Link to post
Share on other sites
20 minutes ago, Billy-3k said:

I guess i must use cloneNode to clone COLOR_CODE and insertBefore to place it where i want, right? Or is there an easier way to achieve that?

I believe you can keep using importNode, it's just that you have to be deliberate about what you copy and where you appendChild it to. Can't just do a simple copy and expect it to transform itself into the shape you want.

20 minutes ago, Billy-3k said:

It sounds a lot of work and kinda hard for me and i'm wondering if it's better to start over with XSLT.

But then you'd have to learn XSLT, and then figure out how to make PHP do it.

  • Like 1

Share this post


Link to post
Share on other sites

I'm new to this and I never tried to change structure of XML files, that's why it sounds hard. I'm still trying to understand how this works!

Thanks for your help, I'll try to import nodes one by one as you said and I'll get back!

Share this post


Link to post
Share on other sites

Try writing out explicit importNode/appendChild calls using the exact XMLs you showed. No loops, no trying to automatically find all nodes, I mean hardcode every call for each individual node. Get it to the point where it produces the output you want.

Then look at the code and see what it looks like as a whole. Like, if you repeat the same couple calls for PRINTING_POSITION stuff three times, that means it should probably be a loop.

Share this post


Link to post
Share on other sites

My 0.02 worth...

Why are you "denormalizing" your data by creating several objects all with the same id and color code. Would it not be more sensible (and a lot easier) to create a merged data set like this...

<PRODUCTINFORMATION>
  <PRODUCTS>
    <PRODUCT>
      <PRODUCT_NUMBER>53-03</PRODUCT_NUMBER>
      <PRODUCT_PRINT_ID>42</PRODUCT_PRINT_ID>
      <PRODUCT_NAME>ProductFirst</PRODUCT_NAME>
      <COLOR_CODE>03</COLOR_CODE>
      <PRINTING_POSITION>
        <ID>TOP BOX</ID>
      </PRINTING_POSITION>
      <PRINTING_TECHNIQUES>
        <PRINTING_TECHNIQUE>
          <ID>DL</ID>
        </PRINTING_TECHNIQUE>
        <PRINTING_TECHNIQUE>
          <ID>L2</ID>
        </PRINTING_TECHNIQUE>
        <PRINTING_TECHNIQUE>
          <ID>P4</ID>
        </PRINTING_TECHNIQUE>
      </PRINTING_TECHNIQUES>
    </PRODUCT>
  </PRODUCTS>
</PRODUCTINFORMATION>

Below is my solution to your original quest using SimpleXML. Hava a go at it on your own as requinix suggested first then peek only if you really get stuck

$xmlA = simplexml_load_file('A.xml');          // product info
$xmlB = simplexml_load_file('B.xml');          // printing info

// create empty output xml object
$xmlC = new simpleXMLElement("<PRODUCTINFORMATION>                
                              </PRODUCTINFORMATION>"); 
$products = $xmlC->addChild("PRODUCTS");

// process file A
foreach ($xmlA->PRODUCTS->PRODUCT as $proda) {
    $prodno = (string)$proda->PRODUCT_NUMBER;
    $prtid = (string)$proda->PRODUCT_PRINT_ID;
    $prodname = (string)$proda->PRODUCT_NAME;
    $color = (string)$proda->COLOR_CODE;
    
    // find related print info xml  from xml file B based on PRODUCT_PRINT_ID
    $prodarr = $xmlB->xpath("PRODUCTS/PRODUCT[PRODUCT_PRINT_ID='$prtid']");
    $prodb = $prodarr[0];
    $prtposid = (string)$prodb->PRINTING_POSITIONS->PRINTING_POSITION->ID;
    
    // build the output xml
    $prodnew = $products->addChild('PRODUCT');
    $prodnew->addChild('PRODUCT_NUMBER', $prodno);
    $prodnew->addChild('PRODUCT_PRINT_ID', $prtid);
    $prodnew->addChild('PRODUCT_NAME', $prodname);
    $prtposns = $prodnew->addChild('PRINTING_POSITIONS');
    
    foreach ($prodb->PRINTING_POSITIONS->PRINTING_POSITION->PRINTING_TECHNIQUE as $tech) {
        $posnew = $prtposns->addChild('PRINTING_POSTION');
        $posnew->addChild('ID', $prtposid);
        $postech = $posnew->addChild('PRINTING_TECHNIQUE');
        $postech->addChild('ID', (string)$tech->ID);
        $posnew->addChild('COLOR_CODE', $color);
    }
}

file_put_contents('C.xml',  $xmlC->asXML() );    // write output to file C

/* C.XML produced by above code  ******************************

        <PRODUCTINFORMATION>
          <PRODUCTS>
            <PRODUCT>
              <PRODUCT_NUMBER>53-03</PRODUCT_NUMBER>
              <PRODUCT_PRINT_ID>42</PRODUCT_PRINT_ID>
              <PRODUCT_NAME>ProductFirst</PRODUCT_NAME>
              <PRINTING_POSITIONS>
                <PRINTING_POSTION>
                  <ID>TOP BOX</ID>
                  <PRINTING_TECHNIQUE>
                    <ID>DL</ID>
                  </PRINTING_TECHNIQUE>
                  <COLOR_CODE>03</COLOR_CODE>
                </PRINTING_POSTION>
                <PRINTING_POSTION>
                  <ID>TOP BOX</ID>
                  <PRINTING_TECHNIQUE>
                    <ID>L2</ID>
                  </PRINTING_TECHNIQUE>
                  <COLOR_CODE>03</COLOR_CODE>
                </PRINTING_POSTION>
                <PRINTING_POSTION>
                  <ID>TOP BOX</ID>
                  <PRINTING_TECHNIQUE>
                    <ID>P4</ID>
                  </PRINTING_TECHNIQUE>
                  <COLOR_CODE>03</COLOR_CODE>
                </PRINTING_POSTION>
              </PRINTING_POSITIONS>
            </PRODUCT>
          </PRODUCTS>
        </PRODUCTINFORMATION>

***************************************************************/

  • Like 1

Share this post


Link to post
Share on other sites

Thank you very much for your help!

I agree it's more sensible and a lot easier but i need this very particular structure that I wrote because it's the only way to import successfully the data using a plugin in Wordpress...

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.