How to get value of specific XML child with PHP - php

I need to get the value of the custom_field "NewTitle" (ID 6) which is nested inside the child "custom_fields" of the following XML:
<issues total_count="63" offset="0" limit="100" type="array">
<issue>
<id>65</id>
<project id="7" name="ProjectName"/>
<tracker id="7" name="TrackerName"/>
<subject>MySubject</subject>
...
<custom_fields type="array">
<custom_field id="4" name="OrderType">
<value>Project</value>
</custom_field>
<custom_field id="26" name="ExtID">
<value>246558</value>
</custom_field>
<custom_field id="25" name="Area" multiple="true">
<value type="array">
<value>Process</value>
<value>System</value>
</value>
</custom_field>
<custom_field id="6" name="NewTitle">
<value>ABCDEF</value>
</custom_field>
...
<custom_field id="20" name="KST">
<value/>
</custom_field>
<custom_field id="11" name="LKF">
<value>3</value>
</custom_field>
<custom_field id="17" name="Link">
<value>XXX</value>
</custom_field>
</custom_fields>
<created_on>2015-12-14T08:00:03Z</created_on>
<updated_on>2016-01-28T09:07:20Z</updated_on>
<closed_on/>
</issue>
</issues>
Can somebody tell me how to do that with PHP? I only managed to display values of the main fields. For example the subject:
foreach ($xml->issue as $issue){
if((string) $issue->tracker['id'] == 7){
echo $issue->subject.'<br/>';
}
}

Use SimpleXML combined with an xpath:
$xml = simplexml_load_string($your_xml_here);
$fields = $xml->xpath("//custom_field[#name='NewTitle']");
foreach ($fields as $field) {
// do sth. useful here
}

You are not very specific about the key(s) to select the <value> you want.
I assume by your example that you want to select...
the <value> of a parent <custom_field>
having the attributes name = "NewTitle" and id = "6"
having an <issue> ancestor with a child <tracker> with an attribute id = "7"
xpath is a fine solution to get to that value directly without iteration. It is like SQL for XML.
select the right <issue>:
/issues/issue[tracker/#id='7']
Note how / is separating parent from child, conditions are within [], attributes have #.
within that issue, select a <custom_field>with the given attributes:
(...)/custom_fields/custom_field[#name='NewTitle'][#id='6']
then get the <value> children (can be more than just 1):
(...)/value
Combined:
$xml = simplexml_load_string($x);
$values = $xml->xpath("/issues/issue[tracker/#id='7']/custom_fields/custom_field[#name='NewTitle'][#id='6']/value");
$values is an array containing single <value> nodes, or an empty array:
foreach ($values as $val)
echo $val->asXML();
Output:
<value>ABCDEF</value>
see it in action: https://eval.in/509835

I don't have a texteditor on hand atm nor am I able to try it out myself but this should do the trick and give you an idea how to itterate through xml nestings.
foreach ($xml->issue as $issue){
foreach ($issue['custom_field'] as $custField){
echo $custField['value'];
}
}

Related

How to get the value of a specific nested XML node in PHP?

I need to get the value of every "custom_field" named "ZIP" out of the following XML-file using PHP.
When I parse it, I always get either the values of all projects or an empty array?
Can somebody help?
<?xml version="1.0" encoding="UTF-8"?>
<projects total_count="237" offset="0" limit="100" type="array">
<project>
<id>239</id>
<name>ABC</name>
<identifier></identifier>
<description></description>
<status>1</status>
<is_public>false</is_public>
<custom_fields type="array">
<custom_field id="18" name="Name affix">
<value></value>
</custom_field>
<custom_field id="20" name="ZIP">
<value>X1111</value>
</custom_field>
</custom_fields>
<created_on>2017-06-05T16:33:13Z</created_on>
<updated_on>2017-06-19T13:46:08Z</updated_on>
</project>
<project>
<id>240</id>
<name>DEF</name>
<identifier></identifier>
<description></description>
<status>1</status>
<is_public>false</is_public>
<custom_fields type="array">
<custom_field id="18" name="Name affix">
<value></value>
</custom_field>
<custom_field id="20" name="ZIP">
<value>Y2222</value>
</custom_field>
</custom_fields>
<created_on>2017-06-05T16:33:14Z</created_on>
<updated_on>2017-06-05T16:33:14Z</updated_on>
</project>
...
I tried the following and get empty arrays:
$projects = simplexml_load_file($rm_host."/projects.xml?key=".$rm_sa_key."&limit=100");
foreach($projects->project as $project){
$zip = $project->xpath('//custom_field[#name="ZIP"]');
print_r($zip);
echo "<br/>";
}
When I try to replace the string with the following, it returns the value of all items, not of the specific one:
zip = $project->xpath('//custom_fields[#type="array"]/custom_field[#name="ZIP"]')
Finally worked after trying and trying.
The correct string to get the specific value was:
$zip = $project->custom_fields->xpath('custom_field[#name="ZIP"]/value')[0];
Without the slash in front of the xpath.

Count how many children are in XML with PHP

i know a few about php, so sorry for the question:
i have this file xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<alert>
<status> </status>
<nothing> </nothing>
<info>
<area>
</area>
</info>
<info>
<area>
</area>
</info>
<info>
<area>
</area>
</info>
</alert>
i must do a for loop and inside a "foreach" for each
The problem is that i'm not sure what is a way to know how many times i had to repeat a for loop. Because in this file xml (that is an example) i don't know how many are
Is good if:
$url = "pathfile";
$xml = simplexml_load_file($url);
$numvulcani = count($xml->alert->info); // is good ?
for ($i = 0; $i <= $numvulcani; $i++) {
foreach ($xml->alert->info[$i] as $entry) {
$area = $entry->area;
}
}
is true ?
sorry for bad english
You need to use SimpleXMLElement::count function for this — It counts the children of an element.
<?php
$xml = <<<EOF
<people>
<person name="Person 1">
<child/>
<child/>
<child/>
</person>
<person name="Person 2">
<child/>
<child/>
<child/>
<child/>
<child/>
</person>
</people>
EOF;
$elem = new SimpleXMLElement($xml);
foreach ($elem as $person) {
printf("%s has got %d children.\n", $person['name'], $person->count());
}
?>
The output will be as follows :
Person 1 has got 3 children.
Person 2 has got 5 children.
Also take a look at this link : xml count using php
Try replacing foreach ($xml->alert->info[$i] as $entry) with:
foreach ($xml->alert->info[$i] as $j => $entry)
The current item index will be $j
You're perhaps overcomplicating this a bit as it's new to you.
First of all, you don't need to reference the alert root element like $xml->alert because the SimpleXMLElement named by the variable $xml represents that document element already.
And second, you don't need to count here, you can just foreach directly:
foreach ($xml->info as $info) {
echo ' * ', $info->asXML(), "\n";
}
This iterates over those three info elements that are children of the alert element.
I recommend the Basic SimpleXML usage guide in the PHP manual for a good start with SimpleXML.

SimpleXML Lower level variable

I'm parsing a file with simple_xml_load_file(), level by level. Here's the sample structure:
<person name="Joe Smith" ...>
<info age="19">
<height val="1.85" />
</info>
<info age="19">
<weight val="82" />
</info>
<info age="19">
<build val="14" />
</info>
</person>
...
As I am parsing I am not going deep, as I don't need to. I do need the age however, without going through each info tag. I need the variables contained in <person> and only the age. How would I go about getting age without another loop?
$persons=$dom->person;
foreach($persons as $person){
$name=$person['name'];
$age=????
}
This should do the job:
foreach($dom->person as $person){
$name=$person['name'];
foreach($person->info as $info) {
echo $info['age'] . '<br>';
}
}
Or if you want to get one age at a specific position:
echo $person->info[0]['age']; // Gets age attribute of first <info> node

parsing xml with php and xpath - getting paths right

I am trying to parse an XML file with php SimpleXML and xpath and having problems getting the paths right and accessing attribute values. Any help appreciated.
Here is my xml file:-
<message:MessageGroup xmlns="http://mynamespace.com">
<Book>
<BookKey>
<Value concept="TITLE" value="Gone Girl"/>
<Value concept="AUTHOR" value="Gillian Flynn"/>
</BookKey>
<Sales>
<Month>Jan</Month>
<Number value="20"/>
</Sales>
<Sales>
<Month>Feb</Month>
<Number value="15"/>
</Sales>
<Sales>
<Month>Mar</Month>
<Number value="30"/>
</Sales>
</Book>
<Book>
<BookKey>
<Value concept="TITLE" value="Inferno"/>
<Value concept="AUTHOR" value="Dan Brown"/>
</BookKey>
<Sales>
<Month>Jan</Month>
<Number value="10"/>
</Sales>
<Sales>
<Month>Feb</Month>
<Number value="15"/>
</Sales>
<Sales>
<Month>Mar</Month>
<Number value="3"/>
</Sales>
</Book>
</message:MessageGroup>
I need to parse this to get the following output:-
Gone Girl,Gillian Flynn,Jan,20
Gone Girl,Gillian Flynn,Feb,15
Gone Girl,Gillian Flynn,Mar30
Inferno,Dan Brown,Jan,10
Inferno,Dan Brown,Feb,15
Inferno,Dan Brown,Mar,3
I have written the following code:-
<?php
$xmlfile = 'Books.xml';
$xml = simplexml_load_file($xmlfile);
$namespace = 'http://mynamespace.com';
$xml->registerXPathNamespace('ns',$namespace);
$books = $xml->xpath('//ns:Book');
//loop through each book -
foreach($books as $book) {
$values = $xml->xpath('ns:BookKey/Value');
$bookkeys= array();
//loop through each Book's BookKey Values and push them to array -
foreach($values as $value){
array_push($bookkeys, $value->attributes()->value);
}
$sales = $xml->xpath('ns:Sales');
//loop through each Book's Sales and write out Month and Number value after the book key values -
foreach($sales as $sale){
foreach($bookkeys as $bk){
echo $bk.",";
}
echo $sale->Month;
echo ",";
echo $sale->Number->attributes()->value;
echo "\n";
}
}
?>
From a var_dump, the $books array appears to be ok; $values and $sales are empty arrays though, so clearly the paths are not right. I have tried leaving out the namespace but still get an empty array; I want to avoid pulling out all the instances of values and sales in the file as opposed to just the ones for each particular book.
You are missing the namespace definition for the prefix message.
To get the value from Value, message:MessageGroup/ns:BookKey/ns:Value/#value will be the XPath to use.

XML parser using shopify webhooked line-items

I am using shopify webhook to update my sql server's 'qty' field when order updated, below is my php code
?php
$xmlData = fopen('php://input' , 'rb');
while (!feof($xmlData)) { $xmlString .= fread($xmlData, 4096); }
fclose($xmlData);
$xml = new SimplexmlElement($xmlString);
file_put_contents('orders/order' . '.xml', $xmlString);
$dom = new DomDocument();
$dom->load('orders/order.xml');
$itemList = $dom->getElementsByTagName('line-item');
foreach($itemList as $item) {
$sku='';
$qty='';
foreach($item->childNodes as $child) {
if ($child->localName == 'sku') {
$sku = $child->textContent;
}
if ($child->localName == 'quantity') {
$qty = $child->textContent;
}
}
mysql_connect ("?????????????????????????");
mysql_select_db("??????????");
$query = "UPDATE xcart_categories SET product_count = product_count - $qty WHERE description='$sku';";
mysql_query($query);
}
and below is xml file i am getting from shopify webhook
<?xml version="1.0" encoding="UTF-8"?>
<order>
<buyer-accepts-marketing type="boolean">true</buyer-accepts-marketing>
<closed-at type="datetime" nil="true"></closed-at>
<currency>USD</currency>
<email>yeongju_l#yahoo.com</email>
<financial-status>pending</financial-status>
<fulfillment-status>fulfilled</fulfillment-status>
<gateway>Local Pick-Up</gateway>
<id type="integer">140303247</id>
<name>#1012</name>
<note></note>
<number type="integer">12</number>
<subtotal-price type="decimal">0.2</subtotal-price>
<taxes-included type="boolean">false</taxes-included>
<total-discounts type="decimal">0.0</total-discounts>
<total-line-items-price type="decimal">0.2</total-line-items-price>
<total-price type="decimal">0.2</total-price>
<total-price-usd type="decimal">0.2</total-price-usd>
<total-tax type="decimal">0.0</total-tax>
<total-weight type="integer">0</total-weight>
<updated-at type="datetime">2012-09-16T21:20:07-04:00</updated-at>
<created-at type="datetime">2012-09-16T21:08:30-04:00</created-at>
<token>dcf523d93c68159c15a7c8d1fabbee07</token>
<landing-site>/products/test</landing-site>
<referring-site></referring-site>
<cancelled-at type="datetime" nil="true"></cancelled-at>
<cancel-reason nil="true"></cancel-reason>
<cart-token>a9a7bc5d8103f6a3bb45e827f0cb8928</cart-token>
<browser-ip nil="true"></browser-ip>
<landing-site-ref nil="true"></landing-site-ref>
<order-number type="integer">1012</order-number>
<discount-codes type="array"/>
<note-attributes type="array">
</note-attributes>
<processing-method>manual</processing-method>
<line-items type="array">
<line-item>
<id type="integer">228531213</id>
<requires-shipping type="boolean">false</requires-shipping>
<fulfillment-service>manual</fulfillment-service>
<grams type="integer">0</grams>
<price type="decimal">0.2</price>
<quantity type="integer">1</quantity>
<sku>1234567</sku>
<title>test</title>
<product-id type="integer">104663831</product-id>
<variant-id type="integer">240660979</variant-id>
<vendor>5 Second</vendor>
<variant-title nil="true"></variant-title>
<fulfillment-status>fulfilled</fulfillment-status>
<name>test</name>
<variant-inventory-management></variant-inventory-management>
<properties type="array">
</properties>
</line-item>
</line-items>
<shipping-lines type="array"/>
<tax-lines type="array">
<tax-line>
<title>NY State Tax</title>
<price type="decimal">0.0</price>
<rate type="float">0.04</rate>
</tax-line>
<tax-line>
<title>Queens County Tax</title>
<price type="decimal">0.0</price>
<rate type="float">0.04875</rate>
</tax-line>
</tax-lines>
<billing-address>
<first-name>Yeongju</first-name>
<last-name>Lee</last-name>
<address1>14809 northern blvd</address1>
<address2></address2>
<city>Flushing</city>
<company></company>
<country>United States</country>
<phone></phone>
<province>New York</province>
<zip>11354</zip>
<latitude type="decimal">40.76529</latitude>
<longitude type="decimal">-73.81831</longitude>
<name>Yeongju Lee</name>
<country-code>US</country-code>
<province-code>NY</province-code>
</billing-address>
<fulfillments type="array">
<fulfillment>
<id type="integer">67712419</id>
<order-id type="integer">140303247</order-id>
<created-at type="datetime">2012-09-16T21:20:07-04:00</created-at>
<updated-at type="datetime">2012-09-16T21:20:07-04:00</updated-at>
<tracking-number nil="true"></tracking-number>
<tracking-company nil="true"></tracking-company>
<status>success</status>
<service>manual</service>
<tracking-url>http://www.google.com/search?q=</tracking-url>
<receipt>
</receipt>
<line-items type="array">
<line-item>
<id type="integer">228531213</id>
<requires-shipping type="boolean">false</requires-shipping>
<fulfillment-service>manual</fulfillment-service>
<grams type="integer">0</grams>
<price type="decimal">0.2</price>
<quantity type="integer">1</quantity>
<sku>1234567</sku>
<title>test</title>
<product-id type="integer">104663831</product-id>
<variant-id type="integer">240660979</variant-id>
<vendor>5 Second</vendor>
<variant-title nil="true"></variant-title>
<fulfillment-status>fulfilled</fulfillment-status>
<name>test</name>
<variant-inventory-management></variant-inventory-management>
<properties type="array">
</properties>
</line-item>
</line-items>
</fulfillment>
</fulfillments>
<customer>
<id type="integer">96489088</id>
<email>yeongju_l#yahoo.com</email>
<accepts-marketing type="boolean">true</accepts-marketing>
<first-name>Yeongju</first-name>
<last-name>Lee</last-name>
<orders-count type="integer">12</orders-count>
<total-spent type="decimal">16.26</total-spent>
<note nil="true"></note>
<created-at type="datetime">2012-08-17T11:31:50-04:00</created-at>
<updated-at type="datetime">2012-09-16T21:20:07-04:00</updated-at>
<state>enabled</state>
<last-order-id type="integer">140303509</last-order-id>
<tags>C</tags>
<last-order-name>#1013</last-order-name>
</customer>
like you see i got two sku and qty because of duplicated line-item so for example when customer order one "product name - test in this case i got "-2" quantity update in my sql server sku test field , but when i am using webhook event when order creation it worked i mean i see only one line item but all the other cases(when order updated, when order payment, when order fullfillment..) show me duplicated line item even there is only one item ordered
i think i am parsing my XML badly
anyone who can teach me correct code to pull 'line items' from the first line-items node i will really appreciate it! Thanks..again
You're doing a lot of redundant steps to parse your XML there. You don't need to save the data into a file before processing it, and you have a stray call to SimpleXML that you're not using. All you need is this:
$xmlString = file_get_contents('php://input');
$dom = new DomDocument();
$dom->loadXML($xmlString);
After that, your parsing logic looks fine but you are only ever running one SQL query, with one SKU in it.
Inside your foreach loop, you define the variables $sku and $qty, but you don't do anything with them inside the loop. So next time round the loop, you will over-write their values, and nothing will ever know about the old values.
There are few ways to do this:
run SQL inside the loop (not very efficient)
build up an array of SKUs and quantities ($sku[] = ...; $qty[] = ...;) and then build your SQL from these arrays
slightly tidier, build a single array with the SKU-quantity pairs as nested arrays ($sku_list[] = array('sku' => ..., 'qty' => ...))
build your SQL string progressively inside the loop ($sql .= '...') and execute it once at the end

Categories