Add element to XML when not existing in child PHP - php

<?xml version="1.0" encoding="UTF-8"?>
<products>
<product>
<a>product a</a>
<b>data</b>
<c>data</c>
</product>
<product>
<a>product b</a>
<c>data</c>
</product>
</products>
When child-element < B > is missing, i want to add it to the XML file. so the < product > ends up like this.No data has to be added, just the element.
<product>
<a>data</a>
<c>data</c>
<b></b>
</product>
can this be done with simplexml ?
<?php
$xml = simplexml_load_file("xml.xml", NULL, TRUE);
foreach ($xml->children() as $child) {}

This is definitely possible. Here's an example of how to do it (assuming you're not worried about the order in which the child element occurs):
$xml = new SimpleXMLElement('xml.xml', NULL, TRUE);
foreach ($xml->children() as $child) {
if (isset($child->b)) {
continue;
}
$child->b = '';
}
// output to new file
$xml->asXML('xml2.xml');
You can also find an online Demo that contains all data:
<?php
/**
* Add element to XML when not existing in child PHP
* #link http://stackoverflow.com/q/19562757/367456
*/
$xml = new SimpleXMLElement('<r><product><b>hello</b></product><product/><product/></r>');
foreach ($xml->children() as $child) {
if (isset($child->b)) {
continue;
}
$child->b = '';
}
$xml->asXML('php://output');
Program Output (beautified):
<?xml version="1.0"?>
<r>
<product>
<b>hello</b>
</product>
<product>
<b></b>
</product>
<product>
<b></b>
</product>
</r>

Related

Merge duplicate content .xml in php

I am learning the php code.
I'm in a situation where it has to be done as soon as possible.
Please help me reorganize my xml duplicate data.
original .xml file
<products>
<product>
<ID>ID1</ID>
<SKU_parent>SKU1</SKU_parent>
<SKU>MA</SKU>
<price>10</price>
<price-sale>5</price-sale>
<KHO1>10</KHO1>
<KHO2>6</KHO2>
<KHO3>2</KHO3>
</product>
<product>
<ID>ID2</ID>
<SKU_parent>SKU2</SKU_parent>
<SKU>MA2</SKU>
<price>500</price>
<price-sale>200</price-sale>
<KHO1>20</KHO1>
<KHO2>0</KHO2>
<KHO3>0</KHO3>
</product>
<product>
<ID>ID3</ID>
<SKU_parent>SKU2</SKU_parent>
<SKU>MA2</SKU>
<price>500</price>
<price-sale>200</price-sale>
<KHO1>0</KHO1>
<KHO2>30</KHO2>
<KHO3>0</KHO3>
</product>
<product>
<ID>ID2</ID>
<SKU_parent>SKU2</SKU_parent>
<SKU>MA2</SKU>
<price>500</price>
<price-sale>200</price-sale>
<KHO1>0</KHO1>
<KHO2>0</KHO2>
<KHO3>40</KHO3>
</product>
into something like this:
<products>
<product>
<ID>ID1</ID>
<SKU_parent>SKU1</SKU_parent>
<SKU>MA</SKU>
<price>10</price>
<price-sale>5</price-sale>
<KHO1>10</KHO1>
<KHO2>6</KHO2>
<KHO3>2</KHO3>
</product>
<product>
<ID>ID2</ID>
<SKU_parent>SKU2</SKU_parent>
<SKU>MA2</SKU>
<price>500</price>
<price-sale>200</price-sale>
<KHO1>20</KHO1>
<KHO2>30</KHO2>
<KHO3>40</KHO3>
</product>
I've tried searching on stackoverflow but it doesn't seem to work.
I appreciate it when someone help me with this php code
For starters, I converted your XML string into an array.
I reduced your array using SKU_parent as a unique key.
I rebuilt the array again into a XML, using the Francis Lewis function (with some slight modifications, by hardcoding the product node if the array key was numeric) - Source: https://stackoverflow.com/a/19987539/4527645
$xml_string = '<products>
<product>
<ID>ID1</ID>
<SKU_parent>SKU1</SKU_parent>
<SKU>MA</SKU>
<price>10</price>
<price-sale>5</price-sale>
<KHO1>10</KHO1>
<KHO2>6</KHO2>
<KHO3>2</KHO3>
</product>
<product>
<ID>ID2</ID>
<SKU_parent>SKU2</SKU_parent>
<SKU>MA2</SKU>
<price>500</price>
<price-sale>200</price-sale>
<KHO1>20</KHO1>
<KHO2>0</KHO2>
<KHO3>0</KHO3>
</product>
<product>
<ID>ID3</ID>
<SKU_parent>SKU2</SKU_parent>
<SKU>MA2</SKU>
<price>500</price>
<price-sale>200</price-sale>
<KHO1>0</KHO1>
<KHO2>30</KHO2>
<KHO3>0</KHO3>
</product>
<product>
<ID>ID2</ID>
<SKU_parent>SKU2</SKU_parent>
<SKU>MA2</SKU>
<price>500</price>
<price-sale>200</price-sale>
<KHO1>0</KHO1>
<KHO2>0</KHO2>
<KHO3>40</KHO3>
</product>
</products>';
$xml = simplexml_load_string($xml_string, 'SimpleXMLElement', LIBXML_NOCDATA);
$json = json_encode($xml);
$xml_array = json_decode($json, true);
$unique = array_reduce($xml_array['product'], function($final, $article){
static $seen = [];
if (!array_key_exists($article['SKU_parent'], $seen)) {
$seen[$article['SKU_parent']] = NULL;
$final[] = $article;
}
return $final;
});
function to_xml(SimpleXMLElement $object, array $data)
{
foreach ($data as $key => $value) {
if (is_array($value)) {
if (is_numeric($key)) {
$new_object = $object->addChild('product');
}
to_xml($new_object, $value);
} else {
if ($key != 0 && $key == (int) $key) {
$key = "key_$key";
}
$object->addChild($key, $value);
}
}
}
$xml = new SimpleXMLElement('<products/>');
to_xml($xml, $unique);
header('Content-Type: text/xml');
print $xml->asXML();
You can also check a working example of this code here: https://onlinephp.io/c/1d06d
This code eliminates the duplicate product nodes by SKU_parent. In order to merge all the values that are not 0, you have to improve the array_reduce or find another method with array_merge_recursive for example. This is just a starting point.
If you're simply using product->ID as the identifier, you could just do:
$xml = new DOMDocument;
$xml->load('dedup.xml');
$xpath = new DOMXpath($xml);
$dupNodes = $xpath->query('/products/product[ID = preceding-sibling::product/ID]');
foreach ($dupNodes as $dupNode) {
$dupNode->parentNode->removeChild($dupNode);
}
echo $xml->saveXML();

echo specific part of xml string

Assume i have the following xml:
<?xml version="1.0" encoding="UTF-8"?>
<products>
<product>
<id>1</id>
<name>Apple iPhone 7</name>
</product>
<product>
<id>2</id>
<name>Samsung Galaxy S7</name>
</product>
</products>
With the following code, I'm able to print the whole xml-string:
$xml = simplexml_load_string($string);
echo '<pre>'.htmlentities($xml->asXML()).'</pre>';
I'm now looking for code to echo a specific part of the xml string, let's say product[0].
I want to be able to echo it as follows:
<product>
<id>1</id>
<name>Apple iPhone 7</name>
</product>
Anyone knows the solution?
You can use this functions:
<?php
function xml2ary(&$string) {
$parser = xml_parser_create();
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
xml_parse_into_struct($parser, $string, $vals, $index);
xml_parser_free($parser);
$mnary=array();
$ary=&$mnary;
foreach ($vals as $r) {
$t=$r['tag'];
if ($r['type']=='open') {
if (isset($ary[$t])) {
if (isset($ary[$t][0])) $ary[$t][]=array(); else $ary[$t]=array($ary[$t], array());
$cv=&$ary[$t][count($ary[$t])-1];
} else $cv=&$ary[$t];
if (isset($r['attributes'])) {foreach ($r['attributes'] as $k=>$v) $cv['_a'][$k]=$v;}
$cv['_c']=array();
$cv['_c']['_p']=&$ary;
$ary=&$cv['_c'];
} elseif ($r['type']=='complete') {
if (isset($ary[$t])) { // same as open
if (isset($ary[$t][0])) $ary[$t][]=array(); else $ary[$t]=array($ary[$t], array());
$cv=&$ary[$t][count($ary[$t])-1];
} else $cv=&$ary[$t];
if (isset($r['attributes'])) {foreach ($r['attributes'] as $k=>$v) $cv['_a'][$k]=$v;}
$cv['_v']=(isset($r['value']) ? $r['value'] : '');
} elseif ($r['type']=='close') {
$ary=&$ary['_p'];
}
}
_del_p($mnary);
return $mnary;
}
// _Internal: Remove recursion in result array
function _del_p(&$ary) {
foreach ($ary as $k=>$v) {
if ($k==='_p') unset($ary[$k]);
elseif (is_array($ary[$k])) _del_p($ary[$k]);
}
}
$string = '<?xml version="1.0" encoding="UTF-8"?>
<products>
<product>
<id>1</id>
<name>Apple iPhone 7</name>
</product>
<product>
<id>2</id>
<name>Samsung Galaxy S7</name>
</product>
</products>';
$requeststring = xml2ary($string);
echo $requeststring["products"]["_c"]["product"][0]["_c"]["id"]["_v"]; //outputs 1
?>
Just change ["products"] or ["product"] or ["id"] for your own result.

PHP - referencing an XML attribute using simpleXML

Using the simplexml_load_file method, I am trying to retrieve and display the text of all name elements (from an XML file below) that have an attribute named "type' with the value of 'tablet.' This foreach loop is only displaying the value of the first element. Any advice? Thanks!
$XMLproducts = simplexml_load_file("products.xml");
foreach($XMLproducts->product->attributes() as $a => $b) {
$i = 0;
if ($b == "Tablet") {
echo $XMLproducts->product[$i]->name;
echo "<br>";
}
}
Here is the XML file:
<products>
<product type="Desktop">
<name>Desktop 1</name>
</product>
<product type="Tablet">
<name>Ipad 1</name>
</product>
<product type="Desktop">
<name>Desktop 2</name>
</product>
<product type="Tablet">
<name>Ipad 2</name>
</product>
</products>
As Scuzzy mentioned in the comments, using SimpleXMLElement::xpath simplifies the solution:
foreach ($XMLproducts->xpath('/products/product[#type="Tablet"]/name') as $name) {
echo $name , "<br>";
}
Try this
$XMLproducts = simplexml_load_file("products.xml");
foreach($XMLproducts->products->product as $product) {
foreach ($product->attributes() as $a => $b) {
$i = 0;
if ($b == "Tablet") {
echo $XMLproducts->product[$i]->name;
echo "<br>";
}
}
}

How to load all xml:lang attributes with SimpleXML

How to read ALL atribute xml:lang values?
Sometimes I do not know how many languages are defined in XMLs data.
<?xml version="1.0" encoding="UTF-8"?>
<offer>
<products>
<product>
<description>
<name xml:lang="eng">English translation</name>
<name xml:lang="lat">Latvian translation</name>
</description>
</product>
<product>
<description>
<name xml:lang="eng">The same English</name>
<name xml:lang="pol">And Polish language</name>
</description>
</product>
</products>
</offer>
I can xml:lang parse in PHP by adding exact language code in xpath
print_r($xml->xpath('products/product/description/name[#xml:lang = "eng"]'));
But I need to add all xml:lang atributes values to parsed array.
Can it be done with PHP SimpleXML?
what about this:
$nodes = $xml->xpath('products/product/description/name[#xml:lang]');
Will return an array of <name>-nodes.
If this is not it, please clarify exactly your desired result.
EDIT
try this to get the xml:lang attributes only:
$langs = $xml->xpath("products/product/description/name[#xml:lang]/#xml:lang");
// $lang is an array of simplexml-elements, transform the values to string like this:
$langs = array_map("strval", $langs);
I'm not 100% on SimpleXML sorry, but I know DomDocument can do what you are after. Hopefully this can be of use to you:
$xmlstring = '<?xml version="1.0" encoding="UTF-8"?>
<offer>
<products>
<product>
<description>
<name xml:lang="eng">English translation</name>
<name xml:lang="lat">Latvian translation</name>
</description>
</product>
<product>
<description>
<name xml:lang="eng">The same English</name>
<name xml:lang="pol">And Polish language</name>
</description>
</product>
</products>
</offer>';
$dom = new DOMDocument();
$dom->loadXML($xmlstring); //or $dom->load('filename.xml');
$xpath = new DOMXPath($dom);
$nodes = $xpath->query('//products/product/description/name');
foreach ($nodes as $node) {
echo 'Language: ' . $node->getAttribute('xml:lang') . '<br />';
echo 'Value: ' . $node->nodeValue . '<br /><br />';
}
You can assign $node->getAttribute('xml:lang') to a variable and run some checks to see if it matches 'eng' or whatever you need.
I used xpath as you had in your original post, but you can also use $dom->getElementsByTagName('name') and access values and attributes in much the same way.
I found easier way to access namespaced attributes. You could use $name->attributes("xml", true) function.
Here is working example:
<?php
$xmlString = '
<products>
<product>
<name xml:lang="eng">
Apples
</name>
<name xml:lang="fr">
Pommes
</name>
</product>
<product>
<name xml:lang="eng">
Strawberries
</name>
<name xml:lang="fr">
Fraises
</name>
</product>
</products>
';
$xml = new \SimpleXMLElement($xmlString);
foreach($xml->product as $product)
{
foreach ($product->name as $name)
{
$attributes = $name->attributes("xml", true);
// print_r($attributes);
foreach ($attributes as $attributeName => $attributeValue)
{
// echo $attributeName . PHP_EOL;
// echo $attributeValue . PHP_EOL;
if ($attributeValue == "eng" && $attributeName == "lang") {
echo "English: " . trim(((string) $name)) . PHP_EOL;
}
if ($attributeValue == "fr" && $attributeName == "lang") {
echo "French: " . trim(((string) $name)) . PHP_EOL;
}
}
}
}
Online demo: https://repl.it/repls/LovingFineDaemon

How do I get to read all the products from XML

I have many categories in my XML file. How can I read all the products in it?
It only reads the first category.
I am including the xml file for you to look at. Here is the PHP code I'm using:
//get products from xml file
foreach($xml->CREATED->CATEGORY as $product){
$atts = $product->PRODUCT->attributes();
$productitemid = $atts['ITEM'];
$title = $product->MODEL;
$rrp = $product->RRP;
$productsdescription = $product->DESCRIPTION;
$prodname = $product->NAME;
echo $productitemid.' - ' ;
// echo $product->id.' - ';
mysql_query("INSERT INTO products (products_id,products_model,products_price,products_status) VALUES ('$productitemid','$title','$rrp','1')");
mysql_query("INSERT INTO products_description (products_id,products_name,products_description) VALUES ('$productitemid','$prodname','$productsdescription')");
}
And here is the XML structure:
<?xml version="1.0" encoding="iso-8859-1"?>
<STOREITEMS>
<CREATED value="Fri Feb 22 1:01:02 GMT 2013">
<CATEGORY id="441" name=" > test1">
<PRODUCT ITEM="12796">
<NAME>test1</NAME>
<MODEL>bb2018</MODEL>
<PRICE>2.28</PRICE>
<RRP>3.99</RRP>
<THUMB>bb2018s.jpg</THUMB>
<IMAGE>bb2018.jpg</IMAGE>
<DESCRIPTION>
test1
</DESCRIPTION>
<POWER/>
<SIZE/>
<ATTRIBUTES NAME="Size" ATTRIBUTEID="2">
<ATTRIBUTEVALUES VALUE="16" TITLE="Small" PRICEADJUST="0.00"/>
<ATTRIBUTEVALUES VALUE="17" TITLE="Medium" PRICEADJUST="0.00"/>
<ATTRIBUTEVALUES VALUE="18" TITLE="Large" PRICEADJUST="0.00"/>
</ATTRIBUTES>
</PRODUCT>
<CATEGORY id="442" name=" > test2">
<PRODUCT ITEM="12805">
<NAME>test2</NAME>
<MODEL>bb2034</MODEL>
<PRICE>0.58</PRICE>
<RRP>1.50</RRP>
<THUMB>bb2034s.jpg</THUMB>
<IMAGE>bb2034.jpg</IMAGE>
<DESCRIPTION>
test2
</DESCRIPTION>
<POWER/>
<SIZE/>
</PRODUCT>
<CATEGORY id="4423" name=" > test3">
<PRODUCT ITEM="13719">
<NAME>test3?</NAME>
<MODEL>BCPG02</MODEL>
<PRICE>2.83</PRICE>
<RRP>4.95</RRP>
<THUMB>bcg02s.jpg</THUMB>
<IMAGE>bcpg02.jpg</IMAGE>
<DESCRIPTION>
test3
</DESCRIPTION>
</PRODUCT>
</CATEGORY>
</CREATED>
</STOREITEMS>
i have done it like this it works. How do i get the products from the categorys then go to the next cateory and get the next porducts the dabase needs them sequence
//i have done it like this it works
$doc = new DOMDocument();
$var = $doc->load('shop.xml');
$root = $doc->documentElement; //root node
$items = $doc->getElementsByTagName('PRODUCT');
$cat = $doc->getElementsByTagName('CATEGORY');
foreach ($cat as $cats){
foreach ($items as $bar)
if ($categoriesid == $b)
$productsid = $bar->getAttribute('ITEM');
$modelcode = $bar->getElementsByTagName('MODEL')->item(0)->nodeValue;
$rrp = $bar->getElementsByTagName('RRP')->item(0)->nodeValue;
$productsdescription = $bar->getElementsByTagName('DESCRIPTION')->item(0)->nodeValue;
$prodname = $bar->getElementsByTagName('NAME')->item(0)->nodeValue;
$categoriesid = $cats->getAttribute('id');
$categoriesname = $cats->getAttribute('name');
}
You could try using DOMDocument like this:
$doc = new DOMDocument();
$var = $doc->load('yourxml.xml');
$root = $doc->documentElement; //root node
$items = $doc->getElementsByTagName('product');
foreach ($items as $bar)
{
$name = $bar->getElementsByTagName('name')->item(0)->nodeValue;
$model = $bar->getElementsByTagName('model')->item(0)->nodeValue;
$price = ....
//do something with the values
}
Please take look on below sample piece of code
hope this help you.
<?php
$xmlString= '<xml Version="1.0">
<created>
<category>
<product id="a"/>
</category>
<category>
<product id="b"/>
</category>
<category>
<product id="c"/>
</category>
<category>
<product id="d"/>
</category>
</created>
</xml>';
$xml = new SimpleXMLElement($xmlString);
foreach ($xml->created->category as $element)
{
foreach($element as $val)
{
echo " product : ".$val->attributes();
}
}
?>
Output of this code will read all product attribute
product : a product : b product : c product : d

Categories