SImpleXML separate values - php

I'm trying to get values from an XML file but i want them to separate in diferrent fields.
My XML file:
<PRODUCTS>
<PRODUCT>
<PRODUCT_NUMBER>7375-06</PRODUCT_NUMBER>
<PRODUCT_NAME>Soft ball</PRODUCT_NAME>
<ITEM_COLOR_NUMBER>04;05;10</ITEM_COLOR_NUMBER>
</PRODUCT>
</PRODUCTS>
My code:
<?php
header ("Content-Type:text/xml");
$xmlA = simplexml_load_file('ftp://.../products.xml');
// create empty output xml object
$final = new simpleXMLElement('<?xml version="1.0" encoding="utf-8"?><PRODUCTINFORMATION></PRODUCTINFORMATION>');
$products = $final->addChild("PRODUCTS");
foreach ($xmlA->PRODUCTS->PRODUCT as $proda) {
$prodbaseno = (string)$proda->PRODUCT_NUMBER;
$prodname = (string)$proda->PRODUCT_NAME;
$prodprintid = (string)$proda->ITEM_COLOR_NUMBER;
// build the output xml
$prodnew = $products->addChild('PRODUCT');
$prodnew->addChild('PRODUCT_NUMBER', $prodbaseno);
$prodnew->addChild('PRODUCT_NAME', $prodname);
$prodnew->addChild('ITEM_COLOR_NUMBER', $prodprintid);
}
echo $final->saveXml();
?>
The output:
<PRODUCTINFORMATION>
<PRODUCTS>
<PRODUCT>
<PRODUCT_NUMBER>MO7375-06</PRODUCT_NUMBER>
<PRODUCT_NAME>Soft ball</PRODUCT_NAME>
<ITEM_COLOR_NUMBER>04;05;10</ITEM_COLOR_NUMBER>
</PRODUCT>
</PRODUCTS>
</PRODUCTINFORMATION>
But what i actually need for ITEM_COLOR_NUMBER is:
<ITEM_COLOR_NUMBER>04</ITEM_COLOR_NUMBER>
<ITEM_COLOR_NUMBER>05</ITEM_COLOR_NUMBER>
<ITEM_COLOR_NUMBER>10</ITEM_COLOR_NUMBER>
How can i set that after a ; create a new ITEM_COLOR_NUMBER with the next value?

You might use explode and use ; as the delimiter.
For example
$prodnew = $products->addChild('PRODUCT');
$prodnew->addChild('PRODUCT_NUMBER', $prodbaseno);
$prodnew->addChild('PRODUCT_NAME', $prodname);
foreach (explode(';', $prodprintid) as $item) {
$prodnew->addChild('ITEM_COLOR_NUMBER', $item);
}
Output
<PRODUCTINFORMATION>
<PRODUCTS>
<PRODUCT>
<PRODUCT_NUMBER>7375-06</PRODUCT_NUMBER>
<PRODUCT_NAME>Soft ball</PRODUCT_NAME>
<ITEM_COLOR_NUMBER>04</ITEM_COLOR_NUMBER>
<ITEM_COLOR_NUMBER>05</ITEM_COLOR_NUMBER>
<ITEM_COLOR_NUMBER>10</ITEM_COLOR_NUMBER>
</PRODUCT>
</PRODUCTS>
</PRODUCTINFORMATION>
Php demo

Related

PHP simplexml_load_file() merge xml fields with same ID

I have 2 XML files for importing products to Woocommerce. Products in both files have the same ID. The only difference is the price field between two XML files.
The first XML is something like this:
<products>
<product>
<product_id>14201</product_id>
<title>some title</title>
<image>some image</image>
<price>10</price>
<category>some category</category>
</product>
</products>
and the second XML file is something like this:
<products>
<product>
<product_id>14201</product_id>
<title>some title</title>
<image>some image</image>
<sale_price>7</sale_price>
<category>some category</category>
</product>
</products>
i want a result like this:
<products>
<product>
<product_id>14201</product_id>
<title>some title</title>
<image>some image</image>
<price>10</price>
<sale_price>7</sale_price>
<category>some category</category>
</product>
</products>
how i can merge all the fields from first XML and only the <sale_price> field from the second XML with PHP simplexml_load_file()?
this is my PHP script:
<?php
$xml = simplexml_load_file("https://mywebsite.com/xmlfile.xml");
echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?><products>";
foreach ($xml->products->product as $prod) {
$pid = $prod->product_id;
$category = $prod->category;
$title = $prod->title;
$image = $prod->image;
$price = $prod->price;
?>
<product>
<id><?php echo $pid;?></id>
.....
.....
</product>
<?php
}
echo "</products>";
?>
You could use XPath to match the product id in the second file, but this could mean re-running a query each product.
This code first reads the second file and extracts all of the sale prices and indexes them by the product id. Then it reads the first file and adds in the sale price when there is a match form the stored array...
$sale = simplexml_load_file("b.xml");
$salePrices = [];
foreach ( $sale->product as $product ) {
$salePrices[(string)$product->product_id] = (string)$product->sale_price;
}
$xml = simplexml_load_file("a.xml");
$key = 0;
foreach ( $xml->product as $product ) {
if ( isset($salePrices[(string)$product->product_id])) {
$xml->product[$key]->sale_price = $salePrices[(string)$product->product_id];
}
$key++;
}
echo $xml->asXML();
(Change file names as required)
I would also go with xpath and just query the update values from the second file for the first one. Example with strings, replace the simple_load function to fit your needs.
Throws in case XML does not parse.
May get bogus results in case product_id evaluates to a different integer value (e.g. empty to 0 and there is a product with the id 0).
foreach (($aXml = simplexml_load_string($a))->xpath(
'/*/product'
) as $p)
foreach (simplexml_load_string($b)->xpath(
sprintf('/*/%s[./product_id = %d]/sale_price', $p->getName(), $p->product_id)
) as $u)
$p->{$u->getName()} = $u
;
Example on 3v4l.org: https://3v4l.org/XStUe

Extracting data from xml using php

I'm trying to grab specific data from a XML file using php.
My goal is to feed a function a "number" and get the corresponding price back.
Eg. if I input the number "swv8813" it will return the price "603.00", and if I input the number "swv8814" it will return "700.00".
How can I do this?
<?xml version="1.0" encoding="iso-8859-1" ?>
<Feed>
<Title>CompanyName</Title>
<Email>info#CompanyName.com</Email>
<Products>
<Product>
<Id>4635</Id>
<Number>swv8813</Number>
<Title><![CDATA[&Tradition - Bellevue AJ2 - Floor Lamp White]]></Title>
<Description><![CDATA[]]></Description>
<Category><![CDATA[Lighting]]></Category>
<Stock>0</Stock>
<Price>603.00</Price>
<Discount>0.00</Discount>
<Created>0000-00-00 00:00:00</Created>
</Product>
<Product>
<Id>4635</Id>
<Number>swv8814</Number>
<Title><![CDATA[&Tradition - Bellevue AJ2 - Floor Lamp Black]]></Title>
<Description><![CDATA[]]></Description>
<Category><![CDATA[Lighting]]></Category>
<Stock>0</Stock>
<Price>700.00</Price>
<Discount>0.00</Discount>
<Created>0000-00-00 00:00:00</Created>
</Product>
</Products>
</Feed>
Use this:
$xmlstr = "<Feed>
<Title>CompanyName</Title>
<Email>info#CompanyName.com</Email>
<Products>
<Product>
<Id>4635</Id>
<Number>swv8813</Number>
<Title><![CDATA[&Tradition - Bellevue AJ2 - Floor Lamp White]]></Title>
<Description><![CDATA[]]></Description>
<Category><![CDATA[Lighting]]></Category>
<Stock>0</Stock>
<Price>603.00</Price>
<Discount>0.00</Discount>
<Created>0000-00-00 00:00:00</Created>
</Product>
<Product>
<Id>4635</Id>
<Number>swv8814</Number>
<Title><![CDATA[&Tradition - Bellevue AJ2 - Floor Lamp Black]]></Title>
<Description><![CDATA[]]></Description>
<Category><![CDATA[Lighting]]></Category>
<Stock>0</Stock>
<Price>700.00</Price>
<Discount>0.00</Discount>
<Created>0000-00-00 00:00:00</Created>
</Product>
</Products>
</Feed>";
$feed = new SimpleXMLElement($xmlstr);
function findPrice($feed, $id){
foreach($feed->Products->Product as $product){
if($product->Number == $id){
return $product->Price;
}
}
return null;
}
echo findPrice($feed, 'swv8813');
echo "\n";
echo findPrice($feed, 'swv8814');
See it working at 3v4l.org script
With DOM and Xpath you can fetch the value directly:
$id = 'swv8813';
$xml = file_get_contents('php://stdin');
$document = new DOMDocument();
$document->loadXml($xml);
$xpath = new DOMXpath($document);
$expression = sprintf(
'number(/Feed/Products/Product[Number="%s"]/Price)',
str_replace(["\00", '"'], '', $id)
);
var_dump($expression, $xpath->evaluate($expression));
Output:
string(54) "number(/Feed/Products/Product[Number="swv8813"]/Price)"
float(603)
The id value is not allowed to have zero bytes or double quotes. A simple str_replace takes care of that.
The expression ...
...fetches all Product nodes...
/Feed/Products/Product
...filters them by their Number child...
/Feed/Products/Product[Number="swv8813"]
...gets the Price children for the filtered nodes...
/Feed/Products/Product[Number="%s"]/Price
...and casts the first found Price to a number.
number(/Feed/Products/Product[Number="swv8813"]/Price)

Parse through XML childs

i have the following xml:
<?xml version="1.0" standalone="yes"?>
<Products>
<Product>
<name>Milk</name>
<price>1.4</price>
<productinfos>
<category1 value="somecategory1"/>
<category2 value="somecategory2"/>
<category3 value="somecategory3"/>
</productinfos>
</Product>
</Products>
how can i make sure that productinfos category1, category2 or category3 do exist and are not an empty string? And how does the loop look like if i want the following output:
//output
Cat1: somecategory1
Cat3: somecategory3
Cat2: somecategory2
because sometimes the xml i parse looks different:
<?xml version="1.0" standalone="yes"?>
<Products>
<Product>
<name>Milk</name>
<price>1.4</price>
<productinfos>
<category1 value=""/>
<category3 value="somecategory"/>
</productinfos>
</Product>
</Products>
in the above example, how can i check if category2 exists?
tia for your efforts!
You're looking for the SimpleXMLElement::children() method.
https://secure.php.net/manual/en/simplexmlelement.children.php
$xml = new SimpleXMLElement(<<<XML
<?xml version="1.0" standalone="yes"?>
<Products>
<Product>
<name>Milk</name>
<price>1.4</price>
<productinfos>
<category1 value="somecategory1"/>
<category2 value="somecategory2"/>
<category3 value="somecategory3"/>
</productinfos>
</Product>
</Products>
XML
);
// $xml is a SimpleXMLElement of <Products>
foreach ($xml->children() as $product) {
if ($product->getName() != 'Product') {
// ignore <Products><Cow> or whatever, if you care
continue;
}
// start out assuming that everything is missing
$missing_tags = array(
'category1' => true,
'category2' => true,
'category3' => true,
);
// iterate through child tags of <productinfos>
foreach ($product->productinfos->children() as $productinfo) {
// element name is accessed using the getName() method, and
// XML attributes can be accessed like an array
if (isset($missing_tags[$productinfo->getName()]) &&
!empty($productinfo['value'])) {
$missing_tags[$productinfo->getName()] = false;
echo $productinfo->getName() . ": " . $productinfo['value'] . "\n";
}
}
// array_filter with one argument filters out any values that eval to false
if (array_filter($missing_tags)) {
echo "Missing tags: " . implode(", ", array_keys($missing_tags)) . "\n";
}
}
The SimpleXML extension is rather less intuitive than the name would suggest, but it's about as simple as you can get with XML...

How should I modify this for each statement to only display entries where all of the values are unique?

foreach($resultXML->products->children() as $product) {
echo "<p>".$product->{'advertiser-name'}." - ".$product->price."</p>
<p>".$product->{'description'}."</p>";
}
Suppose I wanted to screen out the ones that had the same title, and only display the first title that appears in the return results.
I'm not working with my own database, this is all about what's displayed.
I suppose the easiest way would be to keep track of the titles in an array, and checking it each iteration.
$titles = array();
foreach($resultXML->products->children() as $product) {
if (in_array($product->title, $titles) continue;
$titles[] = $product->title;
echo "<p>".$product->{'advertiser-name'}." - ".$product->price."</p>
<p>".$product->{'description'}."</p>";
}
Assuming that the title is contained in $product->title. You could do something fancier through array functions, but I don't see a reason to make a simple problem complicated.
You have not provided any exemplary XML, so given for
<?xml version="1.0" encoding="UTF-8"?>
<example>
<products>
<product>
<title>First Product</title>
<advertiser-name>First Name</advertiser-name>
</product>
</products>
<products>
<product>
<title>Second Product</title>
<advertiser-name>First Name</advertiser-name>
</product>
</products>
<products>
<product>
<title>Third Product</title>
<advertiser-name>Second Name</advertiser-name>
</product>
</products>
</example>
You want to get all product elements with an advertiser-name that is not an advertiser-name of all preceding product elements.
So for the XML above, that would be the 1st and 3rd product element.
You can write that down as an XPath expression:
/*/products/product[not(advertiser-name = preceding::product/advertiser-name)]
And as PHP code:
$xml = simplexml_load_string($buffer);
$expr = '/*/products/product[not(advertiser-name = preceding::product/advertiser-name)]';
foreach ($xml->xpath($expr) as $product) {
echo $product->asXML(), "\n";
}
This produces the following output:
<product>
<title>First Product</title>
<advertiser-name>First Name</advertiser-name>
</product>
<product>
<title>Third Product</title>
<advertiser-name>Second Name</advertiser-name>
</product>
So one answer to your question therefore is: Only query those elements from the document you're interested in. XPath can be used for that with SimpleXMLElement.
Related questions:
Implementing condition in XPath

How to parse deep xml on php

I have problem with that XML parsing:
<pricecatalog>
<pricecathdr></pricecathdr>
<listofcatalogdetails>
<catalogitem>
<product>
<productid>1515159115</productid>
</product>
</catalogitem>
<catalogitem>
<product>
<productid>251541851</productid>
</product>
</catalogitem>
<catalogitem>
<product>
<productid>15181158</productid>
</product>
</catalogitem>
</listofcatalogdetails>
</pricecatalog>
I use DOM to parse it like that:
$doc = new DOMDocument();
$doc->loadXML($this->response);
$items = $doc->getElementsByTagName("catalogitem");
$i = 0;
foreach($items as $itm){
$i++;
}
echo $i;
I think I have to get 2 if parse will success but I allays get 0.

Categories