Explode the attribute in XML File - php

I have an XML with attribute like this:
<products>
<product ProductName="One" ProductCategory="Software::Utilities::Email">
<product ProductName="Two" ProductCategory="Software::Video::Editing">
<product ProductName="Three" ProductCategory="Software::Audio::Converter">
</products>
And how can I explode the "ProductCategory" attribute and separated it like this:
<products>
<product ProductName="One" ProductCategory="Software">
<product ProductName="One" ProductCategory="Utilities">
<product ProductName="One" ProductCategory="Email">
<product ProductName="Two" ProductCategory="Software">
<product ProductName="Two" ProductCategory="Video">
<product ProductName="Two" ProductCategory="Editing">
<product ProductName="Three" ProductCategory="Software">
<product ProductName="Three" ProductCategory="Audio">
<product ProductName="Three" ProductCategory="Converter">
</products>

An Example for you
<?php
$string = <<<XML
<products>
<product ProductName="One" ProductCategory="Software::Utilities::Email"></product>
<product ProductName="Two" ProductCategory="Software::Video::Editing"></product>
<product ProductName="Three" ProductCategory="Software::Audio::Converter"></product>
</products>
XML;
$xml = simplexml_load_string($string);
$obj = json_decode(json_encode($xml), true);
$new_xml = '<products>';
foreach($obj['product'] as $val){
$name = $val['#attributes']['ProductName'];
$pro = explode('::', $val['#attributes']['ProductCategory']);
foreach($pro as $k=>$v){
$new_xml .= '<product ProductName="'.$name.'" ProductCategory="'.$v.'"></product>';
}
}
$new_xml .= '</products>';
$file = fopen("test.xml","w");
fwrite($file, $new_xml);
fclose($file);
?>

You example XML is not valid XML. Make sure to close the product element nodes.
Load the source document into a DOM, create a new target DOM. Import the document element into the target (without child). This will create a copy of that node.
Iterate the product nodes and read the ProductCategory attribute, explode it into an array. Iterate the array an copy the node into the target document (for each value), change the attribute to the value.
$source = new DOMDocument();
$source->loadXml($xml);
$xpath = new DOMXPath($source);
$target = new DOMDocument();
$target->formatOutput = true;
$root = $target->appendChild($target->importNode($source->documentElement));
foreach ($xpath->evaluate('/products/product') as $node) {
$list = explode('::', $node->getAttribute('ProductCategory'));
foreach ($list as $value) {
$newNode = $root->appendChild($target->importNode($node));
$newNode->setAttribute('ProductCategory', $value);
}
}
echo $target->saveXml();
Demo: https://eval.in/209729

Related

PHP - Convert XML to CSV using fputcsv

How do I convert the following XML to CSV using fputcsv
<?xml version="1.0" encoding="utf-8"?>
<PRODUCTINFORMATION>
<SUPPLIER>Company</SUPPLIER>
<PRODUCTS>
<PRODUCT>
<PRODUCT_NUMBER>AR1249-16</PRODUCT_NUMBER>
<PRODUCT_BASE_NUMBER>AR1249</PRODUCT_BASE_NUMBER>
<PRODUCT_ID>10134325</PRODUCT_ID>
</PRODUCT>
<PRODUCT>
<PRODUCT_NUMBER>AR1253-16</PRODUCT_NUMBER>
<PRODUCT_BASE_NUMBER>AR1253</PRODUCT_BASE_NUMBER>
<PRODUCT_ID>10134300</PRODUCT_ID>
</PRODUCT>
<PRODUCT>
<PRODUCT_NUMBER>AR1424-03</PRODUCT_NUMBER>
<PRODUCT_BASE_NUMBER>AR1424</PRODUCT_BASE_NUMBER>
<PRODUCT_ID>10147594</PRODUCT_ID>
</PRODUCT>
</PRODUCTS>
</PRODUCTINFORMATION>
My PHP is:
<?php
$xml = simplexml_load_file('prodinfo.xml');
$csv = fopen('prodinfo.csv', 'w');
foreach($xml->PRODUCTS->PRODUCT as $element) {
foreach($element->PRODUCT_NUMBER as $productNumber){
foreach($element->PRODUCT_BASE_NUMBER as $productBaseNumber){
echo $productNumber ,";", $productBaseNumber. '<BR>' ;
//fputcsv($csv, get_object_vars($productNumber),',','"');
//fclose($csv);
}
}
}
?>
You don't need so many foreach() loops to get each product. It's also easier to build the array from each item as it's fixed and only a few fields...
$csv = fopen('prodinfo.csv', 'w');
fputcsv($csv, ["PRODUCT_NUMBER","PRODUCT_BASE_NUMBER","PRODUCT_ID"]
,',','"');
foreach($xml->PRODUCTS->PRODUCT as $element) {
fputcsv($csv, [(string)$element->PRODUCT_NUMBER,
(string)$element->PRODUCT_BASE_NUMBER,
(string)$element->PRODUCT_ID
],',','"');
}
fclose($csv);

php SimpleXml and xpath: How to acces children inside node that have other namespace?

I am trying to loop through a set of records in an XML document, but cannot access subnodes that are in other namespaces.
this is my php code:
$xml = new SimpleXMLElement($xml);
$ns = $xml->getNamespaces(true);
$xml->registerXPathNamespace('ns', $ns['']);
$xmlRecords = $xml->xpath('//ns:recordData');
foreach ($xmlRecords as $record)
{
$enrichedData = $record->gzd->enrichedData; //Yes! Works!
$originalData = $record->gzd->originalData; //empty
}
The problem is that $orginalData remains empty, probably because al the subnodes inside node originalData belong to other namespaces. My question is: how do I get these subnodes available in my records loop? I need all the nodes that are under the dcterms namespace.
Here is the source xml:
<?xml version="1.0" encoding="UTF-8"?>
<searchRetrieveResponse xmlns="http://www.loc.gov/zing/srw/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.loc.gov/zing/srw/ srw-types.xsd">
<version>1.2</version>
<numberOfRecords>23</numberOfRecords>
<records>
<record>
<recordSchema>http://standaarden.overheid.nl/sru/</recordSchema>
<recordPacking>xml</recordPacking>
<recordData>
<gzd xmlns="http://standaarden.overheid.nl/sru" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:overheid="http://standaarden.overheid.nl/owms/terms/" xmlns:overheidrg="http://standaarden.overheid.nl/cvdr/terms/" xsi:schemaLocation="http://standaarden.overheid.nl/sru gzd.xsd">
<originalData>
<overheidrg:meta>
<owmskern>
<dcterms:identifier>123431_2</dcterms:identifier>
<dcterms:title>Ligplaatsenverordening 2009</dcterms:title>
<dcterms:language>nl</dcterms:language>
<dcterms:type scheme="overheid:Informatietype">regeling</dcterms:type>
<dcterms:creator scheme="overheid:Gemeente">Muiden</dcterms:creator>
<dcterms:modified>2015-08-27</dcterms:modified>
</owmskern>
<owmsmantel>
<dcterms:isFormatOf resourceIdentifier="">Onbekend</dcterms:isFormatOf>
<dcterms:alternative>Ligplaatsenverordening 2009</dcterms:alternative>
<dcterms:source resourceIdentifier="">artikel 149 van de Gemeentewet</dcterms:source>
<dcterms:isRatifiedBy scheme="overheid:BestuursorgaanGemeente">gemeenteraad</dcterms:isRatifiedBy>
<dcterms:subject>ruimtelijke ordening, verkeer en vervoer</dcterms:subject>
<dcterms:issued>2012-12-20</dcterms:issued>
</owmsmantel>
<cvdripm>
<overheidrg:inwerkingtredingDatum>2013-01-01</overheidrg:inwerkingtredingDatum>
<overheidrg:uitwerkingtredingDatum />
<overheidrg:betreft>Onbekend</overheidrg:betreft>
<overheidrg:kenmerk>Onbekend</overheidrg:kenmerk>
<overheidrg:onderwerp />
<overheidrg:gedelegeerdeRegelgeving><al >Geen</al></overheidrg:gedelegeerdeRegelgeving>
<overheidrg:redactioneleToevoeging><al >Geen</al></overheidrg:redactioneleToevoeging>
</cvdripm>
</overheidrg:meta>
</originalData>
<enrichedData>
<organisatietype>Gemeente</organisatietype>
<publicatieurl_xhtml>http://decentrale.regelgeving.overheid.nl/cvdr/xhtmloutput/Historie/Muiden/123431/123431_2.html</publicatieurl_xhtml>
<publicatieurl_xml>http://decentrale.regelgeving.overheid.nl/cvdr/xmloutput/Historie/Muiden/123431/123431_2.xml</publicatieurl_xml>
</enrichedData>
</gzd>
</recordData>
<recordPosition>1</recordPosition>
</record>
</records>
</searchRetrieveResponse>
You can use the SimpleXMLElement children method with the corresponding namespaces like children($ns['overheidrg']) and children($ns['dcterms']).
For example:
$xml = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<searchRetrieveResponse xmlns="http://www.loc.gov/zing/srw/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.loc.gov/zing/srw/ srw-types.xsd">
<version>1.2</version>
<numberOfRecords>23</numberOfRecords>
<records>
<record>
<recordSchema>http://standaarden.overheid.nl/sru/</recordSchema>
<recordPacking>xml</recordPacking>
<recordData>
<gzd xmlns="http://standaarden.overheid.nl/sru" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:overheid="http://standaarden.overheid.nl/owms/terms/" xmlns:overheidrg="http://standaarden.overheid.nl/cvdr/terms/" xsi:schemaLocation="http://standaarden.overheid.nl/sru gzd.xsd">
<originalData>
<overheidrg:meta>
<owmskern>
<dcterms:identifier>123431_2</dcterms:identifier>
<dcterms:title>Ligplaatsenverordening 2009</dcterms:title>
<dcterms:language>nl</dcterms:language>
<dcterms:type scheme="overheid:Informatietype">regeling</dcterms:type>
<dcterms:creator scheme="overheid:Gemeente">Muiden</dcterms:creator>
<dcterms:modified>2015-08-27</dcterms:modified>
</owmskern>
<owmsmantel>
<dcterms:isFormatOf resourceIdentifier="">Onbekend</dcterms:isFormatOf>
<dcterms:alternative>Ligplaatsenverordening 2009</dcterms:alternative>
<dcterms:source resourceIdentifier="">artikel 149 van de Gemeentewet</dcterms:source>
<dcterms:isRatifiedBy scheme="overheid:BestuursorgaanGemeente">gemeenteraad</dcterms:isRatifiedBy>
<dcterms:subject>ruimtelijke ordening, verkeer en vervoer</dcterms:subject>
<dcterms:issued>2012-12-20</dcterms:issued>
</owmsmantel>
<cvdripm>
<overheidrg:inwerkingtredingDatum>2013-01-01</overheidrg:inwerkingtredingDatum>
<overheidrg:uitwerkingtredingDatum />
<overheidrg:betreft>Onbekend</overheidrg:betreft>
<overheidrg:kenmerk>Onbekend</overheidrg:kenmerk>
<overheidrg:onderwerp />
<overheidrg:gedelegeerdeRegelgeving><al >Geen</al></overheidrg:gedelegeerdeRegelgeving>
<overheidrg:redactioneleToevoeging><al >Geen</al></overheidrg:redactioneleToevoeging>
</cvdripm>
</overheidrg:meta>
</originalData>
<enrichedData>
<organisatietype>Gemeente</organisatietype>
<publicatieurl_xhtml>http://decentrale.regelgeving.overheid.nl/cvdr/xhtmloutput/Historie/Muiden/123431/123431_2.html</publicatieurl_xhtml>
<publicatieurl_xml>http://decentrale.regelgeving.overheid.nl/cvdr/xmloutput/Historie/Muiden/123431/123431_2.xml</publicatieurl_xml>
</enrichedData>
</gzd>
</recordData>
<recordPosition>1</recordPosition>
</record>
</records>
</searchRetrieveResponse>
XML;
$xml = new SimpleXMLElement($xml);
$ns = $xml->getNamespaces(true);
$xml->registerXPathNamespace('ns', $ns['']);
$xmlRecords = $xml->xpath('//ns:recordData');
foreach ($xmlRecords as $record)
{
$enrichedData = $record->gzd->enrichedData; //Yes! Works!
$originalData = $record
->gzd
->originalData
->children($ns['overheidrg'])
->meta
->children();
foreach ($originalData as $originalDataItem) {
// Get the children using the namespace 'dcterms'
$children = $originalDataItem->children($ns['dcterms']);
// Check for the name of the xml element
if ($originalDataItem->getName() === "owmskern") {
$identifier = $children->identifier->__toString();
// etc...
}
if ($originalDataItem->getName() === "owmsmantel") {
$isFormatOf = $children->isFormatOf->__toString();
// etc...
}
}
}

SimpleXMLElement output format

I'm using SimpleXMLElement for create datafeed page: Here the code:
$xml = new SimpleXMLElement("<Products />");
foreach ($products as $pro) {
$track = $xml -> addChild('Product');
$track -> addChild('simple_sku', "<![CDATA[ ABC ]]>");
}
Header('Content-type: text/xml');
print($xml -> asXML());
and the output:
<Products>
<Product>
<simple_sku><![CDATA[ ABC ]]></simple_sku>
</Product>
</Products>
I want to know how to turn output into format like this:
<Products>
<Product>
<simple_sku>
<![CDATA[ ABC ]]>
</simple_sku>
</Product>
<Products>
Thanks you a lot.
It's not possible with SimpleXML so I advice you to use DOMDocument.
Usage example:
$dom = new DOMDocument('1.0');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML($simpleXml->asXML());

PHP XML: how to use createCDataSection() properly

I am trying to create an xml dynamically using domDocument.
What I would like to obtain is following:
<?xml version="1.0"?>
<books>
<book>
<content>
<name><![CDATA[dddd]]></name>
</content>
</book>
</books>
Unfortunatelly the script below does not show the output as excepted.
$xml = new DOMDocument('1.0');
$xml->formatOutput = true;
$books = $xml->createElement('books');
$xml->appendChild($books);
$book = $xml->createElement('book');
$books->appendChild($book);
$inside = $xml->createElement('content');
$book->appendChild($inside);
$xml->appendChild($inside)->appendChild($xml->createElement('name'))->appendChild($xml->createCDataSection('dddd'));
echo '<xmp>'.$xml->saveXML().'</xmp>';
This is the output
<?xml version="1.0"?>
<books>
<book>
<content>
<name><![CDATA[dddd]]></name>
<lastname><![CDATA[dddd]]></lastname>
....
</content>
I do not know how to use createCDataSection().
Like this?
$xml = new DOMDocument('1.0');
$xml->formatOutput = true;
$books = $xml->createElement('books');
$xml->appendChild($books);
$book = $xml->createElement('book');
$books->appendChild($book);
$content = $xml->createElement('content');
$book->appendChild($content);
$name = $xml->createElement('name');
$content->appendChild($name);
$name->appendChild($xml->createCDataSection('dddd'));
echo '<xmp>'.$xml->saveXML().'</xmp>';

update/append data to xml file using php

this may sound pretty straight forward, but still I want to post this question in the forum. I have a xml file, which needs to be appended with data after the main element and save the xml file without overwriting the existing xml file but to append the data to already existing data and update the xml file.
For example my xml data looks something similar to this:
<maincontent>
<headercontent>
<product num="2102">
<name>MSG</name>
<category>Wellness</category>
<available content="YES"></available>
</product>
<product num="2101">
<name>YSD</name>
<category>Music</category>
<available content="NO"></available>
</product>
<product num="2100">
<name>RCS</name>
<category>Media</category>
<available content="YES"></available>
</product>
</headercontent>
</maincontent>
I want to add another product with all the info and append the newly added data at the top so that the newly added data should come after the headercontent.
Data to be added:
<product num="2103">
<name>AGB</name>
<category>Movies</category>
<available content="YES"></available>
</product>
The updated xml file should be looking like this as shown below:
<maincontent>
<headercontent>
<product num="2103">
<name>AGB</name>
<category>Movies</category>
<available content="YES"></available>
</product>
<product num="2102">
<name>MSG</name>
<category>Wellness</category>
<available content="YES"></available>
</product>
<product num="2101">
<name>YSD</name>
<category>Music</category>
<available content="NO"></available>
</product>
<product num="2100">
<name>RCS</name>
<category>Media</category>
<available content="YES"></available>
</product>
</headercontent>
</maincontent>
Any useful advice or a piece of example code would be really helpful.
Edit:
sorry guys I haven't posted any php code, my fault. Here is the code which I have been working on:
Thanks
<?php
$xmldoc = new DomDocument();
$xmldoc->formatOutput = true;
$productNum = "2103";
$name = "AGB";
$category = "Movies";
$content = "YES";
if($xml = file_get_contents('main.xml')){
$xmldoc->loadXML($xml);
$root = $xmldoc->firstChild;
$newElement = $xmldoc->createElement('product');
$root->appendChild($newElement);
$numAttribute = $xmldoc->createAttribute("num");
$numAttribute->value = $productNum;
$newElement->appendChild($numAttribute);
$nameElement = $xmldoc->createElement('name');
$root->appendChild($nameElement);
$nameText = $xmldoc->createTextNode($name);
$nameElement->appendChild($nameText);
$categoryElement = $xmldoc->createElement('category');
$root->appendChild($categoryElement);
$categoryText = $xmldoc->createTextNode($category);
$categoryElement->appendChild($categoryText);
$availableElement = $xmldoc->createElement('available');
$root->appendChild($availableElement);
$availableAttribute = $xmldoc->createAttribute("content");
$availableAttribute->value = $content;
$availableElement->appendChild($availableAttribute);
$xmldoc->save('main.xml');
}
?>
My xml file gets updated but the data is added to the firstchild and that too at the bottom, instead I want to add data after and in the beginning as shown above.
Here is my output:
<maincontent>
<headercontent>
<product num="2102">
<name>MSG</name>
<category>Wellness</category>
<available content="YES"/>
</product>
<product num="2101">
<name>YSD</name>
<category>Music</category>
<available content="NO"/>
</product>
<product num="2100">
<name>RCS</name>
<category>Media</category>
<available content="YES"/>
</product>
</headercontent>
<product num="2103"/><name>AGB</name><category>Movies</category><available content="YES"/></maincontent>
Any advice?
This will work.
<?php
$xmldoc = new DomDocument( '1.0' );
$xmldoc->preserveWhiteSpace = false;
$xmldoc->formatOutput = true;
$productNum = "2103";
$name = "AGB";
$category = "Movies";
$content = "YES";
if( $xml = file_get_contents( 'main.xml') ) {
$xmldoc->loadXML( $xml, LIBXML_NOBLANKS );
// find the headercontent tag
$root = $xmldoc->getElementsByTagName('headercontent')->item(0);
// create the <product> tag
$product = $xmldoc->createElement('product');
$numAttribute = $xmldoc->createAttribute("num");
$numAttribute->value = $productNum;
$product->appendChild($numAttribute);
// add the product tag before the first element in the <headercontent> tag
$root->insertBefore( $product, $root->firstChild );
// create other elements and add it to the <product> tag.
$nameElement = $xmldoc->createElement('name');
$product->appendChild($nameElement);
$nameText = $xmldoc->createTextNode($name);
$nameElement->appendChild($nameText);
$categoryElement = $xmldoc->createElement('category');
$product->appendChild($categoryElement);
$categoryText = $xmldoc->createTextNode($category);
$categoryElement->appendChild($categoryText);
$availableElement = $xmldoc->createElement('available');
$product->appendChild($availableElement);
$availableAttribute = $xmldoc->createAttribute("content");
$availableAttribute->value = $content;
$availableElement->appendChild($availableAttribute);
$xmldoc->save('main.xml');
}
?>
Hope this helps.

Categories