PHP Copy xml nodes from one doc to another - php

First of all, I need to Find a parent node by a specific child node value in an xml doc; Then copy some specific children nodes from the parent node to another xml doc.
For instance:
DESTINATION FILE: ('destination.xml')
<item>
<offerStartDate>2012-15-02</offerStartDate>
<offerEndDate>2012-19-02</offerEndDate>
<title>Item Title</title>
<rrp>14.99</rrp>
<offerPrice>9.99</offerPrice>
</item>
and
SOURCE FILE: ('source.xml')
<items>
<item>
<title>Item A</title>
<description>This is the description for Item A</description>
<id>1003</id>
<price>
<rrp>10.00</rrp>
<offerPrice>4.99</offerPrice>
</price>
<offer>
<deal>
<isLive>0</isLive>
</deal>
</offer>
</item>
<item>
<title>Item B</title>
<description>This is the description for Item B</description>
<id>1003</id>
<price>
<rrp>14.99</rrp>
<offerPrice>9.99</offerPrice>
</price>
<offer>
<deal>
<isLive>1</isLive>
</deal>
</offer>
</item>
<item>
<title>Item C</title>
<description>This is the description for Item C</description>
<id>1003</id>
<price>
<rrp>9.99</rrp>
<offerPrice>5.99</offerPrice>
</price>
<offer>
<deal>
<isLive>0</isLive>
</deal>
</offer>
</item>
I want to find the parent node <item> that has it's child node <isLive> value set to "1". Then copy other children nodes of the parent node to the destination xml.
e.g. If parent node <item> has its child node <isLive> set to 1. Copy <title>, <rrp> and <offerPrice> nodes and their values and add them to the destination file as children nodes as shown above.
Pardon my technical lingo if I have not used them correctly.
Many thanks for the help guys!

With SimpleXml (demo):
$dItems = simplexml_load_file('destination.xml');
$sItems = simplexml_load_file('source.xml');
foreach ($sItems->xpath('/items/item[offer/deal/isLive=1]') as $item) {
$newItem = $dItems->addChild('item');
$newItem->addChild('title', $item->title);
$newItem->addChild('rrp', $item->price->rrp);
$newItem->addChild('offerprice', $item->price->offerPrice);
}
echo $dItems->saveXML();
With DOM (demo):
$destination = new DOMDocument;
$destination->preserveWhiteSpace = false;
$destination->load('destination.xml');
$source = new DOMDocument;
$source->load('source.xml');
$xp = new DOMXPath($source);
foreach ($xp->query('/items/item[offer/deal/isLive=1]') as $item)
{
$newItem = $destination->documentElement->appendChild(
$destination->createElement('item')
);
foreach (array('title', 'rrp', 'offerPrice') as $elementName) {
$newItem->appendChild(
$destination->importNode(
$item->getElementsByTagName($elementName)->item(0),
true
)
);
}
}
$destination->formatOutput = true;
echo $destination->saveXml();

Related

Foreach loop through XML tags named tag-1, tag-2, tag-3

How can one loop through XML structure with tags named like
<response>
<tag-1>
<item>
<id>106</id>
<title>DG</title>
</item>
<item>
<id>105</id>
<title>AC</title>
</item>
</tag-1>
<tag-2>
<item>
<id>1</id>
<title>DjG</title>
</item>
<item>
<id>15</id>
<title>AoC</title>
</item>
</tag-2>
</response>
So I load xml with simplexml_load_file, then I tried with xml->children().
$xml = simplexml_load_file("data.xml");
foreach($xml->children() as $kat);
{
var_dump($kat);
}
This has only returned the last element. So I can not loop through all of the tag-* elements.
Any help how to loop through all tags named tag-* will be very appreciated

Merge two variable SimpleXML Objects in PHP

I've been trying to merge two XML files I use to build my menubar in my web application for hours, but I can't get it to work.
I have my main XML file which looks like this:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<root>
<version>1.0.0</version>
<menu>
<Category1>
<item>
<id>Cake</id>
<nr>1</nr>
<hint>I like these</hint>
<userlevel>5</userlevel>
</item>
<item>
<id>Cake 2</id>
<nr>2</nr>
<hint>I like these too, but only for me</hint>
<userlevel>10</userlevel>
</item>
<Category1>
<Category2WithApples>
<item>
<id>Apple Cake</id>
<nr>1</nr>
<hint>Sweet</hint>
<userlevel>5</userlevel>
</item>
<item>
<id>Rainbow Cake</id>
<nr>2</nr>
<hint>Mine!!</hint>
<userlevel>10</userlevel>
</item>
<Category2WithApples>
</menu>
</root>
Now, I want each user to be able to load in his custom XML which is in the same folder as the main.xml which looks like this:
<CategoryMyOwn>
<item>
<id>Item in my Category</id>
<nr>0</nr>
<hint>Some text</hint>
<userlevel>0</userlevel>
</item>
</CategoryMyOwn>
<Category1>
<item>
<id>Item in existing category</id>
<nr>0</nr>
<hint>Some text</hint>
<userlevel>0</userlevel>
</item>
</Category1>
I've tried solutions from
http://php.net/manual/de/ref.simplexml.php
php recursion, function doesn't return any value
http://durgagupta.com.np/php-how-to-merge-two-simplexml-objects/
but they all do not work at all for me or just append the second file to the end of my main.xml. So, my question is, how do I properly merge the user.xml into my main.xml so it looks like this:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<root>
<version>1.0.0</version>
<menu>
<Category1>
<item>
<id>Cake</id>
<nr>1</nr>
<hint>I like these</hint>
<userlevel>5</userlevel>
</item>
<item>
<id>Cake 2</id>
<nr>2</nr>
<hint>I like these too, but only for me</hint>
<userlevel>10</userlevel>
</item>
<item>
<id>Item in existing category</id>
<nr>0</nr>
<hint>Some text</hint>
<userlevel>0</userlevel>
</item>
<Category1>
<Category2WithApples>
<item>
<id>Apple Cake</id>
<nr>1</nr>
<hint>Sweet</hint>
<userlevel>5</userlevel>
</item>
<item>
<id>Rainbow Cake</id>
<nr>2</nr>
<hint>Mine!!</hint>
<userlevel>10</userlevel>
</item>
<Category2WithApples>
<CategoryMyOwn>
<item>
<id>Item in my Category</id>
<nr>0</nr>
<hint>Some text</hint>
<userlevel>0</userlevel>
</item>
</CategoryMyOwn>
</menu>
</root>
Your second XML is not a document, XML documents need to have a document element node. In other words here at the top level only a single element node is allowed. All other element nodes have to be descendants of that node.
You can treat this as an XML fragment however. A fragment is the inner XML of an element node.
In both cases it easier to use DOM for that.
Append a fragment to a parent element node
Let's keep it simple for the first step and append the fragment to the menu node.
$document = new DOMDocument();
$document->loadXml($targetXml);
$xpath = new DOMXpath($document);
$fragment = $document->createDocumentFragment();
$fragment->appendXml($fragmentXml);
foreach ($xpath->evaluate('/root/menu[1]') as $menu) {
$menu->appendChild($fragment);
}
echo $document->saveXml();
The Xpath expression can /root/menu[1] selects the first menu element node inside the root. This can be only one node or none.
A document fragment in DOM is a node object and can be appended like any other node (element, text, ...).
Merging nodes
Merging the category nodes is a little more difficult. But Xpath will help.
$document = new DOMDocument();
$document->loadXml($targetXml);
$xpath = new DOMXpath($document);
$fragment = $document->createDocumentFragment();
$fragment->appendXml($fragmentXml);
$menu = $xpath->evaluate("/root/menu[1]")->item(0);
foreach ($xpath->evaluate('*', $fragment) as $category) {
$targets = $xpath->evaluate("{$category->nodeName}[1]", $menu);
if ($targets->length > 0) {
$targetCategory = $targets->item(0);
foreach ($category->childNodes as $item) {
$targetCategory->appendChild($item);
}
} else {
$menu->appendChild($category);
}
}
echo $document->saveXml();
Fetching the menu node
$menu = $xpath->evaluate("/root/menu[1]")->item(0);
This is about the same like in the first simple example. It fetch the menu nodes in root and returns the first found node. You should check if the list contained a node. But for this example just take it for guaranteed.
Iterating the fragment
foreach ($xpath->evaluate('*', $fragment) as $category) {
...
}
* is a simple Xpath expression that returns any element child node. The fragment can contain other nodes (whitespace, text, comment, ...). The second argument for DOMXpath::evaluate() is the context for the Xpath expression.
Fetching the target category
Next you need to fetch the category node with the same name from the target document. This will return a list with one node or an empty list.
$targets = $xpath->evaluate("{$category->nodeName}[1]", $menu);
if ($targets->length > 0) {
...
} else {
...
}
Append to the found target category
If the category exists append all child nodes from the category in the fragment to the target.
$targetCategory = $targets->item(0);
foreach ($category->childNodes as $item) {
$targetCategory->appendChild($item);
}
Append a category
$menu->appendChild($category);
If the category doesn't exists, just append it to the menu.

Remove one tag and all of it's child nodes of a xml using php

i'm trying to remove one item from a xml with all of it's child nodes but when i excecute following php only the child nodes will be removed.
following is my php
<?php
header('Content-Type: text/xml');
$doc = new DOMDocument('1.0');
$url = '../../data/auction.xml';
$itemNo ="0";
$xml=simplexml_load_file($url);
foreach($xml->xpath('/items/item[itemNumber="'.$itemNo.'"]')as $child)
{
unset($child[0]);
//$child->parentNode->removeChild($node);
}
print $xml->asXML();
?>
the original XML
<?xml version="1.0"?>
<items>
<item>
<itemNumber>0</itemNumber>
<itemName>item1</itemName>
<category>Phones</category>
</item>
<item>
<itemNumber>2</itemNumber>
<itemName>item3</itemName>
<category>laptops</category>
</item>
</items>
Actual output
<items>
<item>
</item>
<item>
<itemNumber>2</itemNumber>
<itemName>item3</itemName>
<category>laptops</category>
</item>
</items>
Desired output
<items>
<item>
<itemNumber>2</itemNumber>
<itemName>item3</itemName>
<category>laptops</category>
</item>
</items>
Please tell me what i'm doing wrong

Need to copy specific XML node from one doc to another

I have two XML files (that have a number of common nodes) that look a bit like these:
DESTINATION FILE: ('destination.xml')
<items>
<item>
<title>Item A</title>
<description>This is the description for Item A</description>
<id>1001</id>
</item>
<item>
<title>Item B</title>
<description>This is the description for Item B</description>
<id>1002</id>
</item>
<item>
<title>Item D</title>
<description>This is the description for Item D</description>
<id>1004</id>
</item>
and
SOURCE FILE: ('source.xml')
<items>
<item>
<title>Item A</title>
<description>This is the description for Item A</description>
<id>1001</id>
</item>
<item>
<title>Item C</title>
<description>This is the description for Item C</description>
<id>1003</id>
</item>
<item>
<title>Item B</title>
<description>This is the description for Item B</description>
<id>1002</id>
</item>
I need to grab the node from SOURCE with the 'id' matching '1003' (in this example) and import it into the DESTINATION. I'm looking for insight in using importNode (or a simpleXML option) and also the xpath in only getting only the node I need.
Just do that and it should work :
<?php
header('Content-type: application/xml'); //Just to test in the browser directly and have a good format
$docSource = new DOMDocument();
$docSource->loadXML(file_get_contents('source.xml'));
$docDest = new DOMDocument();
$docDest->loadXML(file_get_contents('destination.xml'));
$xpath = new DOMXPath($docSource);
$result = $xpath->query('//item[id=1003]')->item(0); //Get directly the node you want
$result = $docDest->importNode($result, true); //Copy the node to the other document
$items = $docDest->getElementsByTagName('items')->item(0);
$items->appendChild($result); //Add the copied node to the destination document
echo $docDest->saveXML();
To get the correct node, I think your XPath should look like this:
$xpath->query('/items/item/id[.="1003"]/..')
To import it to the other document, you'll need to create the document and call importNode with the second parameter set to true:
$newDom = new DOMDocument;
$newDom->load('destination.xml');
$newNode = $newDom->importNode($el, true);
$newDom->firstChild->appendChild($newNode);
file_put_contents('destination.xml', $newDom->saveXML());

PHP: Find XML node and insert child

I have an xml document with the following structure:
<?xml version="1.0" encoding="UTF-8"?>
<items>
<item>
<id>1</id>
<url>www.test.com</url>
</item>
<item>
<id>2</id>
<url>www.test2.com</url>
</item>
</items>
I would like to be able to search for a node value, such as the value of 1 for the id field. Then, once that node is found, select the parent node, which would be < item > and insert a new child within.
I know the concept of using dom document, but not sure how to do it in this instance.
This should be a start:
$dom = new DOMDocument;
$dom->loadXML($input);
$ids = $dom->getElementsByTagName('id');
foreach ($ids as $id) {
if ($id->nodeValue == '1') {
$child = $dom->createElement('tagname');
$child->appendChild($dom->createTextNode('some text'));
$id->parentNode->appendChild($child);
}
}
$xml = $dom->saveXML();
or something close to it.
You can do the same thing in a simpler way. Instead of looking for an <id/> node whose value is 1 then selecting its parent, you can reverse the relation and look for any node which has an <id/> child whose value is 1.
You can do that very easily in XPath, and here's how to do it in SimpleXML:
$items = simplexml_load_string(
'<?xml version="1.0" encoding="UTF-8"?>
<items>
<item>
<id>1</id>
<url>www.test.com</url>
</item>
<item>
<id>2</id>
<url>www.test2.com</url>
</item>
</items>'
);
$nodes = $items->xpath('*[id = "1"]');
$nodes[0]->addChild('new', 'value');
echo $items->asXML();

Categories