how to insert an XML element from one document to another - php

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>

Related

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

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...

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);

Categories