Extracting data from xml using php - 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)

Related

how to insert an XML element from one document to another

first xml
<response status="ok">
<Product>
<name>blbla</name>
<productGroupPrimary>test2</productGroupPrimary>
<productGroupSecondary>test</productGroupSecondary>
<purchasePrice>18</purchasePrice>
<retailPrice>29</retailPrice>
<status>active</status>
<productCode>0001</productCode>
</Product>
<Product>
...
</Product>
other xml
<response status="ok">
<StockQuantityInfo productCode="0001" quantityOnStock="5"></StockQuantityInfo>
<StockQuantityInfo productCode="dhzh" quantityOnStock="5"></StockQuantityInfo>
...
</response>
now i would like to use php to make the final XML document look like this
<response status="ok">
<Product>
<name>blbla</name>
<productGroupPrimary>test2</productGroupPrimary>
<productGroupSecondary>test</productGroupSecondary>
<purchasePrice>18</purchasePrice>
<retailPrice>29</retailPrice>
<status>active</status>
<productCode>0001</productCode>
<stock>5</stock>
</Product>
<Product>
...
</Product>
</response>
i have no idea how i could do this i am a beginner
I tried that foreach
foreach ($xml2->Product as $item2) {
$koda2=$item2->productCode;
foreach ($xml3->StockQuantityInfo as $item3) {
$koda3=$item3->productCode;
if ($koda2 ==$koda3 ) {
$zaloga=$item3->quantityOnStock;
$Product=$xml2->Product->addChild('zaloga',$zaloga);
}
}
the result is that it doesn’t do anything to me, there’s no change, I have a mistake somewhere, or I’m thinking in the wrong direction
I suggest using DOM (not SimpleXML) for this. DOMXpath::evaluate() allows you to fetch the quantity for a product directly using an expression.
// bootstrap the documents
$products = new DOMDocument();
$products->loadXML($productsXML);
$productsXpath = new DOMXpath($products);
$stocks = new DOMDocument();
$stocks->loadXML($stockXML);
$stocksXpath = new DOMXpath($stocks);
// iterate the "Product" elements
foreach ($productsXpath->evaluate('//Product') as $product) {
// get the product code
$code = $productsXpath->evaluate('string(productCode)', $product);
// get the quantity of from the stocks response
$expression = 'number(//StockQuantityInfo[#productCode="'.$code.'"]/#quantityOnStock)';
$quantity = $stocksXpath->evaluate($expression);
// output expression and result for demonstration purposes
var_dump(
$expression, $quantity
);
// validate quantity is a number
if (!is_nan($quantity)) {
// append to "Product"
$product
->appendChild(
$products->createElement('Stock')
)
->textContent = $quantity;
}
}
echo "\n", $products->saveXML();
Output:
string(65) "number(//StockQuantityInfo[#productCode="0001"]/#quantityOnStock)"
float(5)
<?xml version="1.0"?>
<response status="ok">
<Product>
<name>blbla</name>
<productGroupPrimary>test2</productGroupPrimary>
<productGroupSecondary>test</productGroupSecondary>
<purchasePrice>18</purchasePrice>
<retailPrice>29</retailPrice>
<status>active</status>
<productCode>0001</productCode>
<Stock>5</Stock></Product>
</response>

read xml content and add some numbers with php

I have an xml file but through php I would like to make some changes only for some products (each item has its own id.
Let me explain better on some products I would like to add the shipping cost with the price and at the item use grid put from 1 to 0.
<Products>
<Product>
<sku>35</sku>
<sku_manufacturer>test sku</sku_manufacturer>
<manufacturer>test manufacturer</manufacturer>
<ean>800000000000</ean>
<title><![CDATA[title test]]></title>
<description><![CDATA[description</description>
<product_price_vat_inc>8.08</product_price_vat_inc>
<shipping_price_vat_inc>4.99</shipping_price_vat_inc>
<quantity>2842</quantity>
<brand><![CDATA[Finder]]></brand>
<merchant_category><![CDATA[Home/test category]]></merchant_category>
<product_url><![CDATA[https://www.example.com]]></product_url>
<image_1><![CDATA[https://www.example.com]]></image_1>
<image_2><![CDATA[]]></image_2>
<image_3><![CDATA[]]></image_3>
<image_4><![CDATA[]]></image_4>
<image_5><![CDATA[]]></image_5>
<retail_price_vat_inc/>
<product_vat_rate>22</product_vat_rate>
<shipping_vat_rate>22</shipping_vat_rate>
<manufacturer_pdf/>
<ParentSKU/>
<parent_title/>
<Cross_Sell_Sku/>
<ManufacturerWarrantyTime/>
<use_grid>1</use_grid>
<carrier>DHL</carrier>
<shipping_time>2#3</shipping_time>
<carrier_grid_1>DHL</carrier_grid_1>
<shipping_time_carrier_grid_1>2#3</shipping_time_carrier_grid_1>
<carrier_grid_2/>
<shipping_time_carrier_grid_2/>
<carrier_grid_3/>
<shipping_time_carrier_grid_3/>
<carrier_grid_4/>
<shipping_time_carrier_grid_4/>
<carrier_grid_5/>
<shipping_time_carrier_grid_5/>
<DisplayWeight>0.050000</DisplayWeight>
<free_return/>
<min_quantity>1</min_quantity>
<increment>1</increment>
<sales>0</sales>
<eco_participation>0</eco_participation>
<shipping_price_supplement_vat_inc>0</shipping_price_supplement_vat_inc>
<Unit_count>-1.000000</Unit_count>
<Unit_count_type/>
</Product>
</Products>
You XML is a little large so let's strip it down for the example:
$xmlString = <<<'XML'
<Products>
<Product>
<sku>35</sku>
<title><![CDATA[title test]]></title>
<use_grid>1</use_grid>
</Product>
<Product>
<sku>42</sku>
<title><![CDATA[title test two]]></title>
<use_grid>1</use_grid>
</Product>
</Products>
XML;
DOM is a standard API for XML manipulation. PHP supports it and Xpath expressions for fetching nodes.
$document = new DOMDocument('1.0', "UTF-8");
// $document->load($xmlFile);
$document->loadXML($xmlString);
// $xpath for fetching node using expressions
$xpath = new DOMXpath($document);
// iterate "Product" nodes with a specific "sku" child
foreach ($xpath->evaluate('//Product[sku="35"]') as $product) {
// output sku and title for validation
var_dump(
$xpath->evaluate('string(sku)', $product),
$xpath->evaluate('string(title)', $product)
);
// iterate the "use_grid" child elements
foreach ($xpath->evaluate('./use_grid', $product) as $useGrid) {
// output current value
var_dump(
$useGrid->textContent
);
// change it
$useGrid->textContent = "0";
}
}
echo "\n\n", $document->saveXML();
Output:
string(2) "35"
string(10) "title test"
string(1) "1"
<?xml version="1.0"?>
<Products>
<Product>
<sku>35</sku>
<title><![CDATA[title test]]></title>
<use_grid>0</use_grid>
</Product>
<Product>
<sku>42</sku>
<title><![CDATA[title test two]]></title>
<use_grid>1</use_grid>
</Product>
</Products>
Xpath::evaluate()
Xpath::evaluate() fetches nodes using an Xpath expression. The result type depends on the expression. A location path like //Product[sku="35"] will return a list of nodes (DOMNodeList). However Xpath functions inside the can return a scalar value - string(sku) will return the text content of the first sku child node as a string or an empty string.
DOMNode::$textContent
Reading $node->textContent will return all the text inside a node - including inside descendant elements.
Writing it replaces the content while taking care of the escaping.

SImpleXML separate values

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

php xml remove elements not containing a specific word from large file

I am reading an xml file which looks like this but with a lot more products:
<?xml version="1.0" encoding="iso-8859-1"?>
<products>
<product>
<company>company.com</company>
<category>Category A</category>
<brand>Alle!rgica</brand>
<product_name>Name A</product_name>
<productid>6230</productid>
<description>A nice description</description>
<price>125.50</price>
</product>
<product>
<company>Team.com</company>
<category>Category B // something</category>
<brand>New Nordic > Healthcare</brand>
<product_name>Name B</product_name>
<productid>9489</productid>
<description>Active Legs? Buy it now for free</description>
<price>188.00</price>
</product>
</products>
I want to read it and then save it with only products containing the word "free" somewhere in the "product tag" and without the "products" tag and the xml header.
I know how to read the file and save it, but I can't figure out the best approach to remove everything but the products that contain "free".
I tried wth Regex but it didn't seem the best solution (mainly because the matching doesn't properly work):
preg_match_all('/<product>(.*?)(free|free-stuff)(.*?)<\/product>/is', $data, $result);
So in the case of the above the file should only contain:
<product>
<company>Team.com</company>
<category>Category B // something</category>
<brand>New Nordic > Healthcare æøå</brand>
<product_name>Name B</product_name>
<productid>9489</productid>
<description>Active Legs? Buy it now for free</description>
<price>188.00</price>
</product>
use xpath():
$xml = simplexml_load_string($x); // assume XML in $x
$result = $xml->xpath("//product[not(contains(., 'free'))]");
$result contains an array of <product>-nodes as SimpleXML-elements that do not contain "free".
Output:
foreach ($result as $r)
echo $r->asXML();
See it working: https://eval.in/338884
Use this code:
$xml = simplexml_load_file($filename);
foreach($xml->product as $product) {
foreach($product->children() as $child)
// lookup the pattern in all nodes inside product
if ($found = (false !== strpos((string)$child, 'free')))
// Found - we can don't continue searching
break;
// save product found
if ($found) $products[] = $product;
}
print_r( $products);

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

Categories