Replace & Modify XML node in PHP - php

Here's a sample XML structure:
<Products>
<Product>
<Id>1</Id>
<Name>Product 1</Name>
<Category>MEN</Category>
<Category>Women</Category>
<Product>
<Product>
<Id>2</Id>
<Name>Product 2</Name>
<Category>MEN2</Category>
<Category>Women2</Category>
<Product>
</Products>
And I want the file like this:
<Products>
<Product>
<Id>1</Id>
<Name>Product 1</Name>
<CategoryName>MEN:Women</CategoryName>
<Product>
<Product>
<Id>2</Id>
<Name>Product 2</Name>
<CategoryName>MEN:Women</CategoryName>
<Product>
</Products>
So basically it will search through the nodes in products. If it finds "Category", it will change the name to "CategoryName" and concatenate all the sub-sequent category node values into a single one separated by semicolon.
So I have wrote this small PHP, but not sure how to get this to work.
<?php
$xmlFile = "test.xml" //assume the contents are in the file
$xml = simplexml_load_file($xmlFile);
foreach($xml as $item)
{
$name = $item->Product;
if($name->count()) //check if its a "product" node
{
foreach($item as $i)
{
$category = $i->Category;
}
}
}
?>
Can someone point me to the right direction? I haven't much worked with XML.

Please Use this
<?php
$xmlFile = "test.xml"; //assume the contents are in the file
$xml = simplexml_load_file($xmlFile);
$table = '<Products>';
foreach($xml as $item)
{
$table .= '<Product>';
$table .= '<Id>'.$item->Id.'</Id>';
$table .= '<Name>'.$item->Name.'</Name>';
$table .= '<Category>';
$i = 0;
foreach($item->Category as $cat)
{
if($i>0){
$table .= ':';
}
$table .= $cat;
$i++;
}
$table .= '</Category>';
$table .= '</Product>';
}
$table .= '</Products>';
echo $table;
?>

Try this:
$xml = '<Products>
<Product>
<Id>1</Id>
<Name>Product 1</Name>
<Category>MEN</Category>
<Category>Women</Category>
</Product>
<Product>
<Id>2</Id>
<Name>Product 2</Name>
<Category>MEN2</Category>
<Category>Women2</Category>
</Product>
</Products>';
$dom = new DOMDocument();
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML( $xml, LIBXML_NOBLANKS );
$xpath = new DOMXPath( $dom );
$ProductNode = $xpath->query( "//Product" );
if( $ProductNode->length ) {
foreach ( $ProductNode as $node ) {
$category = $node->getElementsByTagName( 'Category' );
$str = '';
// store a reference to the nodes,
// so that they can be deleted later
$del = array();
foreach ( $category as $p ) {
$str .= $p->nodeValue . ':';
$del[] = $p;
}
$str = trim( $str, ':' );
$child = $dom->createElement( 'CategoryName', $str );
$node->appendChild( $child );
foreach ( $del as $p ) {
$p->parentNode->removeChild( $p );
}
}
}
header('content-type: text/xml');
echo $dom->saveXML();
Hope it helps.

Use DomDocument, if you want to modify XML while traversing it:
$xml_obj = new DOMDocument();
$xml_obj->loadXML($xml_string, LIBXML_NOBLANKS );
$xml_obj->preserveWhiteSpace = false;
$xml_obj->formatOutput = true;
$products = $xml_obj->getElementsByTagName('Product');
foreach ($products as $product) {
$cats = array();
$categories = $product->getElementsByTagName('Category');
$tot = $categories->length;
$to_delete = array();
for($i = 0; $i < $tot;$i++) {
$cat = $categories->item($i);
$cats[] = $cat->textContent;
$to_delete[] = $cat;
}
foreach ($to_delete as $delete_node) {
$product->removeChild($delete_node);
}
$product->appendChild($xml_obj->createElement('CategoryName', implode(":", $cats)));
}
print ($xml_obj->saveXML());

Related

create one in xml with php multiple inputs with same name

I have an XML file in which one child has two categories, but with the same name. I want to add one title to each one. How can we do it in PHP?
This is my XML
<root>
<result>
<node>
<title> Some Title Name</title>
<categories>
<category> categor_one </category>
<category> categor_two </category>
</categories>
</node>
<node>
<title> Some Title Name</title>
<categories>
<category> categor_one </category>
<category> categor_tree </category>
</categories>
</node>
</result>
</root>
But I want to obtain this
<root>
<result>
<node>
<title> Some Title Name</title>
<category>categor_one///categor_two </category>
<category1>categor_one///categor_tree</category1>
</node>
</result>
</root>
I managed to impement a function that only gets correctly the category, but if the title is the same it doesn't work as it just creates a new one.
function solve_something($xml, $destination)
{
$xml = simplexml_load_file($xml, "SimpleXMLElement", LIBXML_NOCDATA);
$json = json_encode($xml);
$items = json_decode($json, TRUE);
$products = array();
$product_data = array();
foreach($items['result']['node'] as $item){
$product_data['title'] = $item['title'];
foreach ($item['categories'] as $category) {
if (is_array($category)) {
$product_data['category'] = implode('///', $category);
} else {
$product_data['category'] = $category;
}
}
$products[] = $product_data;
unset($product_data);
}
$path = createXML($products, $destination);
return $path;
}
function createXML($data, $destination)
{
$xmlDoc = new DOMDocument('1.0', 'UTF-8');
$root = $xmlDoc->appendChild($xmlDoc->createElement("root"));
foreach ($data as $key => $product) {
$productA = $root->appendChild($xmlDoc->createElement('product'));
foreach ($product as $key1 => $val) {
if (!empty($val)) {
if ($key1 == 'price' || $key1 == 'tax' || $key1 == 'stockAmount') {
$productA->appendChild($xmlDoc->createElement($key1, $val));
} else {
$ProductKey = $productA->appendChild($xmlDoc->createElement($key1));
$ProductKey->appendChild($xmlDoc->createCDATASection($val));
}
}
}
}
$xmlDoc->formatOutput = true;
fn_rm($destination);
$xmlDoc->save($destination);
return $destination;
}
The output of my code is something like this
<root>
<product>
<title> Some Title Name</title>
<category>categor_one///categor_two </category>
</product>
<product>
<title> Some Title Name</title>
<category>categor_one///categor_tree</category>
</product>
</root>
If you want to keep the data together with the same title, one approach could be to collect that data upfront by using the title as an array key (if it is a valid array key)
When you create the xml, you have the title in the outer foreach loop as the key, and in the inner foreach you can create elements using implode.
Note that in your code you started using product so I took that as a node name.
Example code, which you could use in your code:
$products = array();
$product_data = array();
$xml = simplexml_load_file($xml, "SimpleXMLElement", LIBXML_NOCDATA);
foreach ($xml->result->node as $node) {
$product_data['title'] = (string)$node->title;
foreach($node->categories as $category) {
if (is_array($category)) {
$product_data['category'] = implode('///', $category);
continue;
}
$product_data['category'] = (array)$category->category;
}
$products[(string)$node->title][] = $product_data;
}
$xmlDoc = new DOMDocument('1.0', 'UTF-8');
$root = $xmlDoc->appendChild($xmlDoc->createElement("root"));
foreach ($products as $key => $product) {
$productA = $root->appendChild($xmlDoc->createElement('product'));
$productA->appendChild($xmlDoc->createElement("title", $key));
for ($i = 0; $i < count($product); $i++) {
$productA->appendChild($xmlDoc->createElement("category" . $i, implode("///", $product[$i]["category"])));
}
}
$xmlDoc->formatOutput = true;
echo $xmlDoc->saveXML();
Output
<?xml version="1.0" encoding="UTF-8"?>
<root>
<product>
<title> Some Title Name</title>
<category0> categor_one /// categor_two </category0>
<category1> categor_one /// categor_tree </category1>
</product>
</root>
Php demo

image name in xml child more than one and get limitation with php

i have xml the other array i got it but first one cant how can i solve this
Xml structure like this and get an error code in xml to php First one is not array the second one is array
i Coudnt get the first image children "sitename/11.jpg"
Xml like this
[images] => SimpleXMLElement Object ( [image] => Array ( [0] => sitename/15.jpg [1] => sitename/16.jpg [2] => sitename/17.jpg [3] => sitename/18.jpg ) ) )
[images] => SimpleXMLElement Object ( [image] => sitename/11.jpg ))
<root>
<result>
<node>
<categories>somecategory<categories/>
<images>
<image>sitename/15.jpg</image><image>sitename/16.jpg</image><image>sitename/17.jpg</image><image>sitename/18.jpg</image>
</images>
</node>
<node>
<categories>somecategory<categories/>
<images>
<image>sitename/11.jpg</image>
</images>
</node>
</result>
</root>
function solve_something($xml, $destination)
{
$xml = simplexml_load_file($xml, "SimpleXMLElement", LIBXML_NOCDATA);
$json = json_encode($xml);
$items = json_decode($json, TRUE);
$products = array();
$product_data = array();
$row = 1;
foreach ($items['result']['node'] as $item) {
$product_data['Categories'] = 'categories';
if (isset($item['images']['image'])) {
if (is_array($item['images']['image'])) {
foreach ($item['images']['image'] as $key => $image) {
$key++;
if ($key <= 4) {
$image_name = 'image' . $key;
$product_data[$image_name] = isset($image) ? $image : null;
}
}
} else {
$product_data['image'] = isset($image) ? $image : null;
}
}
$path = createXML($products, $destination);
return $path;
}
The other function code its create the xml file
function createXML($data, $destination)
{
$xmlDoc = new DOMDocument('1.0', 'UTF-8');
$root = $xmlDoc->appendChild($xmlDoc->createElement("root"));
foreach ($data as $key => $product) {
$productA = $root->appendChild($xmlDoc->createElement('product'));
foreach ($product as $key1 => $val) {
if (!empty($val)) {
if ($key1 == 'price' || $key1 == 'tax' || $key1 == 'stockAmount') {
$productA->appendChild($xmlDoc->createElement($key1, $val));
} else {
$ProductKey = $productA->appendChild($xmlDoc->createElement($key1));
$ProductKey->appendChild($xmlDoc->createCDATASection($val));
}
}
}
}
$xmlDoc->formatOutput = true;
fn_rm($destination);
$xmlDoc->save($destination);
return $destination;
}
Code create
<root>
<product>
<categories>somecategory<categories/>
<images>
<image1>sitename/15.jpg
<image2>sitename/16.jpg
<image3>sitename/17.jpg
</images>
</product>
<product>
<categories>somecategory<categories/>
<images>
<image1>sitename/15.jpg
<image2>sitename/16.jpg
<image3>sitename/17.jpg
<image4>sitename/18.jpg
</images>
</product>
</root>
But i want
<root>
<product>
<categories>somecategory<categories/>
<images>
<image1>sitename/15.jpg
<image2>sitename/16.jpg
<image3>sitename/17.jpg
</images>
</product>
<product>
<categories>somecategory<categories/>
<images>
<image1>sitename/11.jpg
</images>
</product>
</root>
There are a few issues with the code
If you want to have 3 images, this part if ($key <= 4) { should be lesser or equal than 2.
You don't really have to return anything (or you want to check for false), as you are writing a file, as the save function returns the number of bytes written or false if an error occurred.
Using $key++; like this in the foreach can also be done using a for loop where you can use $i to append after image
Not sure why you want to use createCDATASection but I have left that part out to get the desired result
As you have multiple sections of node, you may use an $product_data array per iteration to add the values to and after the foreach add $product_data to the $products array to prevent overwriting the values for every loop.
The updated code might look like
function solve_something($xml, $destination)
{
$xml = simplexml_load_string($xml, "SimpleXMLElement", LIBXML_NOCDATA);
$json = json_encode($xml);
$items = json_decode($json, TRUE);
$products = array();
foreach ($items['result']['node'] as $item) {
$product_data = array();
$category = $item["categories"];
$product_data["categories"] = $category;
if (isset($item['images']['image'])) {
if (is_array($item['images']['image'])) {
for ($i = 0; $i < count($item['images']['image']); $i++) {
if ($i < 3) $product_data["image" . ($i + 1)] = $item['images']['image'][$i];
}
} else $product_data["image1"] = $item['images']['image'];
}
$products[] = $product_data;
}
createXML($products, $destination);
}
function createXML($data, $destination)
{
$xmlDoc = new DOMDocument('1.0', 'UTF-8');
$root = $xmlDoc->appendChild($xmlDoc->createElement("root"));
foreach ($data as $key => $product) {
$productA = $root->appendChild($xmlDoc->createElement('product'));
$imagesElm = $xmlDoc->createElement('images');
foreach ($product as $key1 => $val) {
if ($key1 == 'price' || $key1 == 'tax' || $key1 == 'stockAmount' || $key1 === "categories") {
$productA->appendChild($xmlDoc->createElement($key1, $val));
} elseif (substr($key1, 0, 5) === "image") {
$imagesElm->appendChild($xmlDoc->createElement($key1, $val));
}
}
$productA->appendChild($imagesElm);
}
$xmlDoc->formatOutput = true;
$xmlDoc->save($destination);
}
$xml = <<<XML
<?xml version="1.0" encoding="utf-8"?>
<root>
<result>
<node>
<categories>somecategory</categories>
<images>
<image>sitename/15.jpg</image>
<image>sitename/16.jpg</image>
<image>sitename/17.jpg</image>
<image>sitename/18.jpg</image>
</images>
</node>
<node>
<categories>somecategory</categories>
<images>
<image>sitename/11.jpg</image>
</images>
</node>
</result>
</root>
XML;
solve_something($xml, "result.xml");
The xml in result.xml looks like
<?xml version="1.0" encoding="UTF-8"?>
<root>
<product>
<categories>somecategory</categories>
<images>
<image1>sitename/15.jpg</image1>
<image2>sitename/16.jpg</image2>
<image3>sitename/17.jpg</image3>
</images>
</product>
<product>
<categories>somecategory</categories>
<images>
<image1>sitename/11.jpg</image1>
</images>
</product>
</root>

Delete Node isn't working with Simple XML (PHP)

I want to delete a node if the title of an node is matching a filter (array). I use unset() and I already tried $node and $item but both arguments won't delete my node...
What is wrong in this code? - I do enter the if condition, because I see in if in my console!
$dom = new DOMDocument('1.0', 'utf-8');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->load("shop1.xml");
$pathXML = "/products/product";
$titleArray = array("Test", "Battlefield 1");
$doc = simplexml_import_dom($dom);
$items = $doc->xpath($pathXML);
foreach ($items as $item) {
$node = dom_import_simplexml($item);
$title = $node->getElementsByTagName('title')->item(0)->textContent;
echo $title . "\n";
foreach ($titleArray as $titles) {
echo $titles . "\n";
if (mb_stripos($title, $titles) !== false) {
echo "in if\n\n";
unset($item);
}
}
}
$dom->saveXML();
$dom->save("shop1_2.xml");
XML File:
<products>
<product>
<title>Battlefield 1</title>
<url>https://www.google.de/</url>
<price>0.80</price>
</product>
<product>
<title>Battlefield 2</title>
<url>https://www.google.de/</url>
<price>180</price>
</product>
</products>
Greetings and Thank You!
All you're doing is unsetting a local variable. Instead you need to alter the DOM:
$dom = new DOMDocument('1.0', 'utf-8');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->load("shop1.xml");
$xpathQuery = "/products/product";
$titleArray = array("Test", "Battlefield 1");
$xp = new DomXpath($dom);
$items = $xp->query($xpathQuery);
foreach ($items as $item) {
$title = $item->getElementsByTagName('title')->item(0)->textContent;
echo "$title\n";
if (in_array($title, $titleArray)) {
$item->parentNode->removeChild($item);
}
}
$dom->saveXML();
$dom->save("shop1_2.xml");

Manipulating nodes in DomDocument and transform it into an array

I've an XML looking this way :
<?xml version="1.0" ?>
<rss version="2.0">
<channel>
<title>get_news_category</title>
<item>
<id>10502</id>
<title>Cheesecake</title>
<summary>SummaryBlahblah</summary>
</item>
<item>
<id>13236</id>
<title>Moto</title>
<summary>summary blahblah</summary>
</item>
And I want to put the items into an php array.
I've done so far:
$nodes = $dom->getElementsByTagName('item')->item(0);
$values = $nodes->getElementsByTagName("*");
$articles = array();
foreach ($values as $node) {
$articles[$node->nodeName] = $node->nodeValue;
}
var_dump($articles);
Which only returns me in an array, the 1 <item> element. which is logic because i told him ->item(0).
So how to select all the items in order to put all the items into an array ?
Thanks.
use $nodes->length
$dom = new DOMDocument();
$dom->loadHTML($html);
$nodes = $dom->getElementsByTagName('item');
for($i=0; $i<$nodes->length; $i++){
$values = $nodes->item($i)->getElementsByTagName("*");
$articles = array();
foreach ($values as $num => $node) {
$articles[$i][$node->nodeName] = $node->nodeValue;
}
var_dump($articles);
}
You need to iterate the $nodes.
$nodes = $dom->getElementsByTagName('item');
for ($i = 0; $i < $nodes->length; $i++)
{
// Lets grab the node
$values = $nodes->item($i)->getElementsByTagName("*");
}

How do extract child element in XML using DOM in PHP 5.0?

I am having the XML like this
<?xml version="1.0" encoding="utf-8"?>
<root>
<mynode catid="10" catname="Animals" label="Animals" catdesc="" parent_id="2">
<mynode catid="11" catname="Lions" label="Lions" catdesc="" parent_id="10">
<mynode catid="12" catname="lion" label="lion" catdesc="" parent_id="11"/>
<mynode catid="13" catname="lioness" label="lioness" catdesc="" parent_id="11"/>
</mynode>
</mynode>
</root>
From this I want to remove
<?xml version="1.0" encoding="utf-8"?>
<root>
and
</root>
So expected result is
<mynode catid="10" catname="Animals" label="Animals" catdesc="" parent_id="2">
<mynode catid="11" catname="Lions" label="Lions" catdesc="" parent_id="10">
<mynode catid="12" catname="lion" label="lion" catdesc="" parent_id="11"/>
<mynode catid="13" catname="lioness" label="lioness" catdesc="" parent_id="11"/>
</mynode>
</mynode>
How can I do this?
Edit 1:TO Phil
$dom = new DomDocument();
//$dom->preserveWhitespace = false;
$dom->load('treewithchild.xml');
function DOMinnerHTML($element)
{
$innerHTML = "";
$children = $element->childNodes;
foreach ($children as $child)
{
$tmp_dom = new DOMDocument();
$tmp_dom->appendChild($tmp_dom->importNode($child, true));
$innerHTML.=trim($tmp_dom->saveXML());
echo $tmp_dom->saveXML();
}
return $innerHTML;
}
$dom->preserveWhiteSpace = false;
$domTable = $dom->getElementsByTagName("mynode");
foreach ($domTable as $tables)
{
//echo $tables;
DOMinnerHTML($tables);
}
As you want the inner markup of the <root> node, that is the element who's child nodes you'll want to iterate. You can access this element using the DOMDocument::documentElement property.
Try this (tested and working)
$doc = new DOMDocument;
$doc->load('treewithchild.xml');
$inner = '';
foreach ($doc->documentElement->childNodes as $child) {
$inner .= $doc->saveXML($child);
}
echo $inner;
I expect that the root element is returned also, you have to know that for each xml file an is added impliicitly, even if it exists in your file. so try to do this
$children = $element->childNodes->childNodes;
i think that would help you.

Categories