How to get the xml sub-parameters?
how to get all the results of category and return a result $category .. category = Test1, Test2
my xml
<xml>
<title>Test 123</title>
<categories>
<category>Test1</category>
<category>Test2</category>
</categories>
</xml>
my get code
$category = htmlspecialchars($item->categories->category, ENT_XML1 | ENT_QUOTES, 'UTF-8');
echo $category; //I want to return Test1, Test2
You could loop $item->categories->category using a foreach:
$source = <<<DATA
<xml>
<title>Test 123</title>
<categories>
<category>Test1</category>
<category>Test2</category>
</categories>
</xml>
DATA;
$item = simplexml_load_string($source);
foreach ($item->categories->category as $elm) {
echo $elm . PHP_EOL;
}
That will give you:
Test1
Test2
See a php demo
This is what are you searching for?
$xml = new SimpleXMLElement('<item id="1234">
<property name="country_id">
<value>4402</value>
</property>
<property name="rc_maintenance_other">
</property>
<property name="claim_right_shareholder">
</property>
<property name="charges_other">
</property>
<property name="other_expenses_heating">
</property>
<property name="unpaid_bills_amount">
</property>
<property name="iv_person_phone">
<value>03-6756711</value>
</property>
</item>
');
foreach ($xml->xpath('//item[#id="1234"]') as $item)
{
foreach ($item->children() as $child) {
echo $child['name'] ."\n";
}
}
duplicate question?
Access all children of a certain node with simplexml and php
Related
Can anyone help me with read large file?
I reading and item like that:
$xmlReader = new XMLReader();
$xmlReader->open($path);
while ($xmlReader->read() && $xmlReader->name !== 'item') ;
while ($xmlReader->name == 'item')
{
$node = new SimpleXMLElement($xmlReader->readOuterXML());
foreach($node->properties as $property)
{
var_dump($property->price); //empty class SimpleXMLElement
var_dump($property->attributes()); //empty class SimpleXMLElement
}
$xmlReader->next('item');
}
$xmlReader->close();
So i cant to read $property->price because i receive empty class SimpleXMLElement
The same situation with attribute sku i also receive empty class SimpleXMLElement
And i can't convert in (string) because i receive empty string.
How to read the children node?
Source XML:
<items>
<item>
<code>be274178-9039-11e6-86d0-001e6727034e</code>
<delete>0</delete>
<title>Полотенцесушитель М-обр. 500*500 нар. р. 1" арт.00004-5050</title>
<category>13760cb9-8f7b-11e6-86d0-001e6727034e</category>
<producer>5a457cfd-b088-11e2-9c54-001e6727034e</producer>
<properties>
<property sku="40 451">
<price>2831.00</price>
<characteristics>
<characteristic>
<title>Свойство</title>
<value>накопление</value>
<filter>0</filter>
<visible>0</visible>
</characteristic>
<characteristic>
<title>Форма</title>
<value>М-образный</value>
<filter>1</filter>
<visible>1</visible>
</characteristic>
</characteristics>
</property>
<property sku="40 464">
<price>3442.00</price>
<characteristics>
<characteristic>
<title>Свойство</title>
<value>накопление</value>
<filter>0</filter>
<visible>0</visible>
</characteristic>
</characteristics>
</property>
</properties>
</item>
</items>
It should be
foreach($node->properties->property as $property)
because it's only one <properties> elements but many <property> elements.
See Example #4 Accessing non-unique elements in SimpleXML in the PHP Manual:
When multiple instances of an element exist as children of a single parent element, normal iteration techniques apply.
<?php
include 'example.php';
$movies = new SimpleXMLElement($xmlstr);
/* For each <character> node, we echo a separate <name>. */
foreach ($movies->movie->characters->character as $character) {
echo $character->name, ' played by ', $character->actor, PHP_EOL;
}
?>
I have a situation that's driving me crazy. I want to search my feed for an element with a certain value (deriving from an array) and if that value is found, change the value of it's previous sibling element.
My feed looks like this
<products>
<product>
<properties>
<property name="category">
<value>Fruits</value>
</property>
<property name="item">
<value>Banana</value>
</property>
</properties>
</product>
<product>
<properties>
<property name="category">
<value>Fruits</value>
</property>
<property name="item">
<value>Apple</value>
</property>
</properties>
</product>
<product>
<properties>
<property name="category">
<value>Fruits</value>
</property>
<property name="item">
<value>Carrot</value>
</property>
</properties>
</product>
</products>
As you can see, there can be some errors in the feed. For these instances i made an array with the appropriate value, like so:
$replacements = Array(
"Carrot" => "Vegetable"
);
Now i thought to select every property with the attribute item that has the value as in $replacements's key, then select the previous sibling element with the attribute category and change this value with the matching $replacements's value.
I came up with this, but that only gives me a white screen with no output at all
$xml_src = 'feed.xml';
$document = new DOMDocument();
$document->load($xml_src);
$xpath = new DOMXpath($document);
$query = '//property[#name = "item"]';
$entries = $xpath->query($query);
foreach ($entries as $entry) {
if(array_key_exists($entry->nodeValue,$replacements)){
$entry->previousSibling->previousSibling->nodeValue = $replacements[$entries->nodeValue];
}
}
But i don't understand why it doesn't output anything
There's a couple things a bit off here:
array_key_exists($entry->nodeValue,$replacements)
The nodeValue is going to contain all the text content within the property element and its descendants - including newlines and spaces.
... = $replacements[$entries->nodeValue];
You're likely looking for $replacements[$entry->nodeValue] but again you've got the same problem as above with the white space. Not only that but you'd be replacing the entirety of the prior property's nodeValue with text so:
<property name="category">
<value>Fruits</value>
</property>
would become just:
<property name="category">Vegetable</property>
To fix it all up I'd recommend adjusting the query, as well as how you're addressing the target value to replace in order to get rid of the ->previousSibling->previousSibling chaining and be a bit more explicit.
Example:
foreach ($xpath->query('//property[#name="item"]/value') as $node) {
if (array_key_exists(trim($node->textContent), $replacements)) {
$target = $xpath->query(
'preceding-sibling::property/value/text()',
$node->parentNode
)->item(0);
$target->parentNode->replaceChild(
$dom->createTextNode($replacements[trim($node->textContent)]),
$target
);
}
}
echo $dom->saveXML();
Output:
...
<product>
<properties>
<property name="category">
<value>Vegetable</value>
</property>
<property name="item">
<value>Carrot</value>
</property>
</properties>
</product>
</products>
You need to return Value and in order to do that you need to correct your query to this
$query = '//property[#name = "item"]/value';
You can try to use the complete path, so:
$query = '//products/product/properties/property[#name = "item"]/value';
$entries = $xpath->query($query);
foreach ($entries as $entry) {
if(array_key_exists($entry->nodeValue,$replacements)){
$entry->previousSibling->previousSibling->nodeValue = $replacements[$entries->nodeValue];
}
}
Also, you can try to use a context (Manual):
$context = $document->getElementsByTagName('products')->item(0);
$query = './/property[#name = "item"]/value';//add a '.' before the query to make it relative to the context
$entries = $xpath->query($query, $context);
foreach ($entries as $entry) {
if(array_key_exists($entry->nodeValue,$replacements)){
$entry->previousSibling->previousSibling->nodeValue = $replacements[$entries->nodeValue];
}
}
Both solutions work for me in very similar situations.
Preface: There are many questions similar to this that I've scoured through profusely. Nothing can quite answer what I need to do.
Here is my data structure. (obviously simplified)
<Upload>
<tickets>
<ticket>
<ticket_number>123</ticket_number>
<clerk>001</clerk>
<property>
<item>
<item_number>1</item>
<jewelry_metal>GOLD</jewelry_metal>
</item>
<item>
<item_number>2</item>
<jewelry_metal>SILVER</jewelry_metal>
</item>
</property>
</ticket>
<ticket>
<ticket_number>456</ticket_number>
<clerk>001</clerk>
<property>
<item>
<item_number>1</item>
<jewelry_metal></jewelry_metal>
</item>
</property>
</ticket>
<tickets>
<Upload>
I need to check the first item in each ticket, and if the <jewelry_metal> tag is empty, delete the entire <ticket>. I'm open to any suggestions at this point.
Your best bet here would be to use XPath and DomDocument. Try this:
<?php
$xmlstr = <<<XML
<?xml version='1.0'?>
<Upload>
<tickets>
<ticket>
<ticket_number>123</ticket_number>
<clerk>001</clerk>
<property>
<item>
<item_number>1</item_number>
<jewelry_metal>GOLD</jewelry_metal>
</item>
<item>
<item_number>2</item_number>
<jewelry_metal>SILVER</jewelry_metal>
</item>
</property>
</ticket>
<ticket>
<ticket_number>456</ticket_number>
<clerk>001</clerk>
<property>
<item>
<item_number>1</item_number>
<jewelry_metal></jewelry_metal>
</item>
</property>
</ticket>
</tickets>
</Upload>
XML;
$doc = new DOMDocument();
$doc->loadxml($xmlstr);
$xpath = new DOMXpath($doc);
foreach($xpath->query('//tickets/ticket/property/item[1]') as $key => $item) {
if (empty($item->getElementsByTagName('jewelry_metal')->item(0)->nodeValue)) {
$item->parentNode->removeChild($item);
}
}
echo $doc->saveXML();
The output will be:
<?xml version="1.0"?>
<Upload>
<tickets>
<ticket>
<ticket_number>123</ticket_number>
<clerk>001</clerk>
<property>
<item>
<item_number>1</item_number>
<jewelry_metal>GOLD</jewelry_metal>
</item>
<item>
<item_number>2</item_number>
<jewelry_metal>SILVER</jewelry_metal>
</item>
</property>
</ticket>
<ticket>
<ticket_number>456</ticket_number>
<clerk>001</clerk>
<property>
</property>
</ticket>
</tickets>
</Upload>
Hope this helps!
I would suggest you use the Xpath language for that. Lets translate:
I need to check the first item in each ticket, and if the <jewelry_metal> tag is empty, delete the entire <ticket>
into xpath:
//ticket[.//item[1]/jewelry_metal = ""]
That will query all elements to delete. To run such an xpath query in PHP with simplexml and then delete the elements is explained in this question:
PHP SimpleXML - Remove xpath node
Ensure you've got error reporting enabled because your XML you've provided in question is totally broken. Just in case you didn't notice.
I have been asked to parse a simple file which is stored as an XML file, the data is to be then put into a mysql database.
However I have absolutely no clue what to do and after looking online all the examples given seem either too complicated for my problem or not the right solution. The XML file looks like this:
<shop>
<products>
<product id="1" name="Cornetto" price="1.20" description="Traditional Cornetto" />
<product id="2" name="Smarties" price="1.00" description="Smarties Icecream" />
</products>
<stocks>
<stock id="1" amount="242" price="pounds" />
<stock id="2" amount="11" price="pounds" />
</stocks>
I've tried looking at SimpleXML and I think that's the direction I have to go but I just have no idea.
Any help or pointers would be great.
I personally like the normal XMl formatting so I changed it since its a bit more readable but this is how you can use it:
$xmlstr = <<<XML
<?xml version='1.0' standalone='yes'?>
<shop>
<products>
<product>
<id>1</id>
<name>Cornetto</name>
<price>1.20</price>
<description>Traditional Cornetto</description>
</product>
<product>
<id>2</id>
<name>Smarties</name>
<price>1.00</price>
<description>Smarties Icecream</description>
</product>
</products>
<stocks>
<stock>
<id>1</id>
<amount>242</amount>
<price>pounds</price>
</stock>
<stock>
<id>2</id>
<amount>11</amount>
<price>pounds</price>
</stock>
</stocks>
</shop>
XML;
Handling part:
$xml = new SimpleXMLElement($xmlstr);
echo 'single value: <br />';
echo $xml->products->product[0]->id; // get single value
echo '<br /><br />';
//Loop trough multiple products
echo 'multiple values: <br />';
foreach($xml->products->product as $product)
{
echo $product->id.' - ';
echo $product->name.' - ';
echo $product->price.' - ';
echo $product->description;
echo '<br/>';
}
Assuming the file is called data.xml
$string = file_get_contents('data.xml') reads the entire file into $string.
$xml = new SimpleXMLElement($string); parses that string, and converts it into an object tree similar to the actual document. So if that's the document -
<root>
<b>
<c>first</c>
<c>second</c>
</b>
</root>
The SimpleXMLElement object would be used like:
$xml->b // gets all children of b (c[0] and c[1])
print $xml->b->c[0] // gets the first c, will print "first"
You can use for example SimpleXMLElement and xpath
<?php
$xmlStr = <<<EOF
<?xml version="1.0"?>
<shop>
<products>
<product id="1" name="Cornetto" price="1.20" description="Traditional Cornetto" />
<product id="2" name="Smarties" price="1.00" description="Smarties Icecream" />
</products>
<stocks>
<stock id="1" amount="242" price="pounds" />
<stock id="2" amount="11" price="pounds" />
</stocks>
</shop>
EOF;
$xml=new SimpleXMLElement($xmlStr);
// get product line with xpath for example
$products=$xml->xpath("/shop/products/product");
if ($products) {
// loop over each product node
foreach ($products as $product) {
// do whatever you want with the data
echo("id=>".$product["id"].", name=>".$product["name"]."<br/>");
}
}
// same for stock
// get product line with xpath for example
$stocks=$xml->xpath("/shop/stocks/stock");
if ($stocks) {
// loop over each product node
foreach ($stocks as $stock) {
// do whatever you want with the data
echo("id=>".$stock["id"].", amount=>".$stock["amount"]."<br/>");
}
}
?>
$xml = simplexml_load_file($filename);
foreach($xml->product as $product) {
foreach($product->attributes() as $name => $attribute) {
echo "$name = $attribute";
}
}
$xml = simplexml_load_file($filename);
foreach($xml->products->product as $not)
{
foreach($not->attributes() as $a => $b)
{
echo $a,'="',$b,"\"<br />";
}
}
So I am working with an API that returns results in XML. Let's just say for argument sake I am returned the following:
<?xml version="1.0" encoding="UTF-8"?>
<Properties>
<Property>
<Name>Joes Crab Shack</Name>
<Address>111 Shack Street</Address>
</Property>
<Property>
<Name>Johns Shoe Store</Name>
<Address>123 Shoe Avenue</Address>
</Property>
</Properties>
Now I am using PHP and I get the results into a variable. So essentially this happens:
$xml_results = '<?xml version="1.0" encoding="UTF-8"?><Properties><Property><Name>Joes Crab Shack</Name><Address>111 Shack Street</Address></Property><Property><Name>Johns Shoe Store</Name><Address>123 Shoe Avenue</Address></Property></Properties>';
Now how can I treat this as an XML document and for example loop through it and print out all property names?
Something like this should get the job done.
$request_xml = '<?xml version="1.0" encoding="UTF-8"?>
<Properties>
<Property>
<Name>Joes Crab Shack</Name>
<Address>111 Shack Street</Address>
</Property>
<Property>
<Name>Johns Shoe Store</Name>
<Address>123 Shoe Avenue</Address>
</Property>
</Properties>';
$xml = simplexml_load_string($request_xml);
$i = 0;
while ($xml->Property[$i])
{
echo $xml->Property[$i]->Name;
echo $xml->Property[$i]->Address;
$i++;
}
Deserialize into an xml tree, try SimpleXML. That way you can access that data in a more convenient fashion and grab specific xml elements..