PHP - Converting XML to array misses some information from XML string - php

I am using curl to get an external XML document and convert it to an array.
The code works almost perfect, apart from one node in the XML isn't being processed and put in my array.
Under <drivers> there is a code for each driver: <driver code="TM1"> but this doesn't get picked up by my array as an #attribute array like the others such as <collection date="20160324">
Does anybody know why this is happening?
XML:
<findit xmlns="http://www.URL.COM" version="8" type="TIX" date="20160323">
<account code="XXXXXX">
<customers>
<customer code="12345">
<status code="0">Success</status>
<logistic-jobs>
<logistic-job id="12345" date="20160324" status="PLA" modified="201603231420">
<number>
<number1>479599</number1>
<number3>11221</number3>
</number>
<collection date="20160324" time="0500">
<name>JOHN SMITH</name>
<address1>UNIT 3 DAVEY ROAD</address1>
<address2>FIELDS END BUSINESS PARK</address2>
<address3>GOLDTHORPE</address3>
<address4>ROTHERHAM</address4>
<address5>S63 0JF</address5>
</collection>
<delivery date="20160324" time="1200">
<address1>EXAMPLE</address1>
<address2>GLENEAFLES FARM</address2>
<address3>GLENEAGLES CLOSE</address3>
<address4>STANWELL, MIDDLESEX</address4>
<address5>TW19 7PD</address5>
</delivery>
<extra>
<address1>45FT C/SIDER</address1>
<address2>No</address2>
<address4>CEMENT</address4>
</extra>
<drivers>
<driver code="TM1">DAVE SMITH (DAYS)</driver>
</drivers>
<load weight="27600.00" volume="0.00">
<pallets full="23" half="0" quarter="0" blue="0" oversize="0"/>
</load>
</logistic-job>
</logistic-jobs>
</customer>
</customers>
</account>
</findit>
PHP:
$job_array = json_decode(json_encode(simplexml_load_string($xml)), true);
if(is_array($job_array['account']['customers']['customer'])) {
// Foreach customer in array
foreach($job_array['account']['customers']['customer'] as $i => $customer) {
// If status is set to success
if($customer['status'] == "Success") {
// For each job
foreach($customer['logistic-jobs']['logistic-job'] as $i => $job) {
echo '<pre>'; print_r($job); echo '</pre>';
}
}
}
}
OUTPUT:
Array
(
[#attributes] => Array
(
[id] => 12345
[date] => 20160324
[status] => PLA
[modified] => 201603231420
)
[number] => Array
(
[number1] => 479599
[number3] => 11221
)
[collection] => Array
(
[#attributes] => Array
(
[date] => 20160324
[time] => 0500
)
[name] => JOHN SMITH
[address1] => UNIT 3 DAVEY ROAD
[address2] => FIELDS END BUSINESS PARK
[address3] => GOLDTHORPE
[address4] => ROTHERHAM
[address5] => S63 0JF
)
[delivery] => Array
(
[#attributes] => Array
(
[date] => 20160324
[time] => 1200
)
[address1] => EXAMPLE
[address2] => GLENEAFLES FARM
[address3] => GLENEAGLES CLOSE
[address4] => STANWELL, MIDDLESEX
[address5] = TW19 7PD
)
[extra] => Array
(
[address1] => 45FT C/SIDER
[address2] => No
[address4] => CEMENT
)
[drivers] => Array
(
[driver] => DAVE SMITH (DAYS)
)
[load] => Array
(
[#attributes] => Array
(
[weight] => 21509.00
[volume] => 0.00
)
[pallets] => Array
(
[#attributes] => Array
(
[full] => 52
[half] => 0
[quarter] => 0
[blue] => 0
[oversize] => 0
)
)
)
)

I have a simple answer for you: don't convert XML to an array. SimpleXML has a really useful API for traversing through the XML document and finding the data you want; throw away the horrible json_decode(json_encode( hack and look at the examples in the PHP manual.
In this case, echo $driver would give you the contents (driver name) and echo $driver['code'] would give you the attribute value; clearly, a plain array can't have that convenient magic, which is why converting to one is giving you problems.
Just remember that print_r will also hide things from you, and you might want a dedicated debugging function.
Here's an example (with live demo) of getting the code and name of each driver:
$job_simple = simplexml_load_string($xml);
if(isset($job_simple->account->customers->customer)) {
// Foreach customer in array
foreach($job_simple->account->customers->customer as $i => $customer) {
// If status is set to success
if((string)$customer->status == "Success") {
// For each job
foreach($customer->{'logistic-jobs'}->{'logistic-job'} as $i => $job) {
echo "<ul>\n";
foreach ( $job->drivers->driver as $driver ) {
echo "<li> {$driver['code']}: $driver\n";
}
echo "</ul>\n";
}
}
}
}

Related

Get data from OMDb API with xml

I'l try to get the movie title and info from the omdb API. This is my code:
<?php
$enter = $_GET["enter"];
$content = file_get_contents("https://www.omdbapi.com/?s=$enter&r=xml");
$xml = simplexml_load_string($content);
if($xml) {
echo "<h2>" .$xml->title. "</h2>";
}
else
{
echo "Nothing found. Add the info manualy";
}
?>
The "enter" value is from the search form with AJAX. He create only an empty h2 tag. How can i get also the data from the API?
Thank you,
Julian
You should familiarize yourself with the structure of the xml to know how to access its elements. print_r(get_object_vars($xml)) will show you a structure like this:
Array
(
[#attributes] => Array
(
[totalResults] => 3651
[response] => True
)
[result] => Array
(
[0] => SimpleXMLElement Object
(
[#attributes] => Array
(
[title] => World War Z
[year] => 2013
[imdbID] => tt0816711
[type] => movie
[poster] => https://images-na.ssl-images-amazon.com/images/M/MV5BMTg0NTgxMjIxOF5BMl5BanBnXkFtZTcwMDM0MDY1OQ##._V1_SX300.jpg
)
)
[1] => SimpleXMLElement Object
(
[#attributes] => Array
(
[title] => Captain America: Civil War
[year] => 2016
[imdbID] => tt3498820
[type] => movie
[poster] => https://images-na.ssl-images-amazon.com/images/M/MV5BMjQ0MTgyNjAxMV5BMl5BanBnXkFtZTgwNjUzMDkyODE#._V1_SX300.jpg
)
)
...
...
...
[9] => SimpleXMLElement Object
(
[#attributes] => Array
(
[title] => War
[year] => 2007
[imdbID] => tt0499556
[type] => movie
[poster] => https://images-na.ssl-images-amazon.com/images/M/MV5BMTgzNTA4MTc3OF5BMl5BanBnXkFtZTcwOTA0ODk0MQ##._V1_SX300.jpg
)
)
)
)
So you receive an array with results where you need to pick from. Alternatively if you know the exact title the API has the t=title option which only returns a single result (see documentation).
So assuming you use the s=title option which returns multiple results, you can use something like this to pick information from the first result:
<?php
$enter = $_GET["enter"];
$content = file_get_contents("https://www.omdbapi.com/?s=$enter&r=xml");
$xml = simplexml_load_string($content);
# show the structure of the xml
# print_r(get_object_vars($xml));
if($xml) {
print "<h2>" .$xml->result[0]['title']. "</h2>";
print "<br>imdbID=" . $xml->result[0]['imdbID'] ;
} else {
echo "Nothing found. Add the info manualy";
}
?>

Iterate through multidimensional PHP array and output values

I'm having a real headache trying to iterate through an array and output elements. Using the array structure below I want to be able to output each instance of partname.
The following loop outputs the first instance of partname. I can't seem to adapt it to loop through all instances within the array. I'm sure I'm missing something basic.
foreach($ItemsArray['assignments'] as $item) {
$partname = $item['grades'][0]['partname'];
}
Array
(
[assignments] => Array
(
[0] => Array
(
[assigntmentid] => 5101
[grades] => Array
(
[0] => Array
(
[id] => 5101
[name] => Advanced AutoCad
[partid] => 6601
[partname] => Draft
[userid] => 82069
[grade] => 53
[courseid] => 6265
[fullname] => Computer Aided Design
)
)
)
[1] => Array
(
[assigntmentid] => 5101
[grades] => Array
(
[0] => Array
(
[id] => 5101
[name] => Advanced AutoCad
[partid] => 6602
[partname] => Final
[userid] => 82069
[grade] => 35
[courseid] => 6265
[fullname] => Computer Aided Design
)
)
)
)
)
Instead of just coding by slapping the keyboard. Write down what your function needs to do. In english (or whatever language you prefer). This would be something like:
Foreach assignment, loop over all grades and store the partname of
that grade into an array.
And then code it:
function getPartnames($assignments) {
$partNames = array();
foreach ($assignments as $assignment) {
foreach($assignment['grades'] as $grade) {
$partNames[] = $grade['partname'];
}
}
return $partNames;
}
So what did I do? I simply translated english to code.
Some few more tips: Use variables names that make sense. $item; $ItemArray; ... don't make sense. They tell me nothing
use an extra foreach in your loop:
foreach($ItemsArray['assignments'] as $item) {
foreach($item['grades'] as $grade) {
echo $grade['partname'];
}
}

Why is my SimpleXMLElement data truncated?

I retrieve the following XML data:
<?xml version="1.0" encoding="UTF-8"?>
<JMF xmlns="http://www.CIP4.org/JDFSchema_1_1" MaxVersion="1.4" SenderID="HP Hybrid Elk JMF" TimeStamp="2014-02-19T07:42:11+00:00" Version="1.4" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="JMFRootMessage">
<!--Generated by the CIP4 Java open source JDF Library version : CIP4 JDF Writer Java 1.4a BLD 63-->
<Response ID="Rgdhhhdfhd" ReturnCode="0" Type="KnownDevices" refID="gdhhhdfhd" xsi:type="ResponseKnownDevices">
<DeviceList>
<DeviceInfo DeviceCondition="OK" DeviceID="HPSSPP-SM" DeviceStatus="Running" StatusDetails="Running"/>
<DeviceInfo CounterUnit="Impressions" DeviceCondition="OK" DeviceID="192.168.1.101" DeviceStatus="Running" ProductionCounter="12345678" StatusDetails="Indigo: Printing" xmlns:jdf="http://www.CIP4.org/JDFSchema_1_1">
<GeneralID IDUsage="hpCustomerImpressionCounter" IDValue="12345678.0"/>
</DeviceInfo>
<DeviceInfo CounterUnit="Impressions" DeviceCondition="OK" DeviceID="192.168.1.102" DeviceStatus="Running" ProductionCounter="23456789" StatusDetails="Indigo: Printing" xmlns:jdf="http://www.CIP4.org/JDFSchema_1_1">
<GeneralID IDUsage="hpCustomerImpressionCounter" IDValue="23456789.0"/>
</DeviceInfo>
</DeviceList>
</Response>
</JMF>
I load it into a SimpleXMLElement:
<?php
$jdf_response = new SimpleXMLElement($xml_response);
And I can then display it like so:
<pre>
<?php print_r($jdf_response->Response->DeviceList); ?>
</pre>
Which gives the following output:
SimpleXMLElement Object
(
[DeviceInfo] => Array
(
[0] => SimpleXMLElement Object
(
[#attributes] => Array
(
[DeviceCondition] => OK
[DeviceID] => HPSSPP-SM
[DeviceStatus] => Running
[StatusDetails] => Running
)
)
[1] => SimpleXMLElement Object
(
[#attributes] => Array
(
[CounterUnit] => Impressions
[DeviceCondition] => OK
[DeviceID] => 192.168.1.101
[DeviceStatus] => Running
[ProductionCounter] => 12345678
[StatusDetails] => Indigo: Printing
)
[GeneralID] => SimpleXMLElement Object
(
[#attributes] => Array
(
[IDUsage] => hpCustomerImpressionCounter
[IDValue] => 12345678.0
)
)
)
[2] => SimpleXMLElement Object
(
[#attributes] => Array
(
[CounterUnit] => Impressions
[DeviceCondition] => OK
[DeviceID] => 192.168.1.102
[DeviceStatus] => Running
[ProductionCounter] => 23456789
[StatusDetails] => Indigo: Printing
)
[GeneralID] => SimpleXMLElement Object
(
[#attributes] => Array
(
[IDUsage] => hpCustomerImpressionCounter
[IDValue] => 23456789.0
)
)
)
)
)
So far so good. But I need to get the data from the DeviceInfo array, so I modify the code:
<pre>
<?php print_r($jdf_response->Response->DeviceList->DeviceInfo); ?>
</pre>
But instead of three SimpleXMLElement objects, I get only the first.
SimpleXMLElement Object
(
[#attributes] => Array
(
[DeviceCondition] => OK
[DeviceID] => HPSSPP-SM
[DeviceStatus] => Running
[StatusDetails] => Running
)
)
What am I doing wrong?
Update:
The reason I was using print_r() in the first place because because I was getting no output from the following code:
<?php
$addresses = array();
foreach ($jdf_response->Response->DeviceList->DeviceInfo as $device) {
$addresses[] = $device->{'#attributes'}'DeviceID'];
}
print_r($addresses);
Example #4 Accessing non-unique elements in SimpleXML
When multiple instances of an element exist as children of a single parent element,
normal iteration techniques apply.
Data is still there, You just have to use an iterator like:
foreach($jdf_response->Response->DeviceList->DeviceInfo as $device)
{
print_r($device);
}
Reference

php multi dimension array?

I am retrieving a multidimensional php array (I think) from an API, now all of the values return perfectly and when I dump the array with print_r I get this:
Event Object
(
[title] => test
[link] => google.com
[updated] => 2013-03-06T12:08:56.521-05:00
[id] => test
[name] => Copy of Copy of Copy of Mar 05, 2013 - TEST4
[description] =>
[registered] => 2
[createdDate] => 2013-03-06T12:08:56.521-05:00
[status] => COMPLETE
[eventType] => OTHER
[eventLocation] => EventLocation Object
(
[location] => test
[addr1] => test
[addr2] =>
[addr3] =>
[city] => madrid
[state] => andalucia
[country] =>
[postalCode] => 06103
)
[registrationUrl] => https://google.com
[startDate] => 2013-03-07T13:00:00-05:00
[endDate] => 2013-03-07T13:00:00-05:00
[publishDate] => 2013-03-06T12:11:15.958-05:00
[attendedCount] => 0
[cancelledCount] => 0
[eventFeeRequired] => FALSE
[currencyType] => USD
[paymentOptions] => Array
(
)
[registrationTypes] => Array
(
[0] => RegistrationType Object
(
[name] =>
[registrationLimit] =>
[registrationClosedManually] =>
[guestLimit] =>
[ticketing] =>
[eventFees] => Array
(
)
)
)
)
Now bumbling through wit my basic PHP i have found that i can list all of the first array items from [title] to [eventType] like this:
<?php
// get details for the first event returned
$Event = $ConstantContact->getEventDetails($events['events'][0]);
reset($Event);
while (list($key, $value) = each($Event)) {
echo "$key => $value \r\n<br/>";
}
?>
my question: All I need to do it retrieve [title] and [startDate] I don't need the rest now I could just hide the rest using Js and css but i am sure i am just being an idiot and there is an easier way to traverse this array so it only spits out the two values i need.
How do i do this?
You do not have to traverse the whole object. Just access the properties you want:
$title = $Event->title;
$startDate = $Event->startDate;
// or
echo $Event->title;
echo $Event->startDate;
It's actually an object - not an (associative) array!
What's the difference?
An object is an instance of a class. A class has methods and attributes (member variables).
Unlike C++ or some other OOP languages, you can define attributes dynamically without declaring them in the class declaration.
An array is simply a container for keys and their values.
It seems that it's not an array but an object so something like this:
echo $Event->title;
echo $Event->startDate;
Is it ...
<?php
// get details for the first event returned
$Event = $ConstantContact->getEventDetails($events['events'][0]);
reset($Event);
echo $Event->$title . "<br/>";
echo $Event->$startDate . "<br/>";
?>
? Or am I too simple?

Access index number of a node inside an XML file

For a structure such as
SimpleXMLElement Object
(
[id] => https://itunes.apple.com/us/rss/topfreeapplications/limit=2/genre=6014/xml
[title] => iTunes Store: Top Free Applications in Games
[updated] => 2013-02-04T07:18:54-07:00
[icon] => http://itunes.apple.com/favicon.ico
[entry] => Array
(
[0] => SimpleXMLElement Object
(
[updated] => 2013-02-04T07:18:54-07:00
[id] => https://itunes.apple.com/us/app/whats-word-new-quiz-pics-words/id573511269?mt=8&uo=2
[title] => What is the Word? - new quiz with pics and words - RedSpell
)
[1] => SimpleXMLElement Object
(
[updated] => 2013-02-04T07:18:54-07:00
[id] => https://itunes.apple.com/us/app/temple-run-2/id572395608?mt=8&uo=2
[title] => Temple Run 2 - Imangi Studios, LLC
)
)
)
I'm using the following code to target the entry node as each entry node stands for a game.
$xml = simplexml_load_file('the path to file');
foreach ($xml->entry as $val)
{
$gameTitle = $val->title;
$gameLink = $val->id;
}
WHAT I'M LOOKING FOR
Target the index of the entry node, i.e. 0, 1, 2 and so on; i.e.
[0] => SimpleXMLElement Object // <-- this fella here, capture 0
(
[updated] => 2013-02-04T07:18:54-07:00
[id] => https://itunes.apple.com/us/app/whats-word-new-quiz-pics-words/id573511269?mt=8&uo=2
[title] => What is the Word? - new quiz with pics and words - RedSpell
)
[1] => SimpleXMLElement Object // <-- this fella here, capture 1
(
[updated] => 2013-02-04T07:18:54-07:00
[id] => https://itunes.apple.com/us/app/temple-run-2/id572395608?mt=8&uo=2
[title] => Temple Run 2 - Imangi Studios, LLC
)
Whatever I do, I just can't seem to get the index of the current node.
UPDATE
Just for you people to test it out Code Viper
You are looking for a function called iteator_to_array setting the second parameter to false:
$entries = iterator_to_array($xml->entry, false);
foreach ($entries as $index => $val)
{
$gameTitle = $val->title;
echo "<p>$gameTitle</p><p>Index = $index</p>";
}
Demo. Actually you do not must use that function you could also just count:
$index = 0;
foreach ($xml->entry as $val)
{
echo "<p>{$val->title}</p><p>Index = $index</p>";
$index++;
}
By default the $key (as in your example code) is the tagname of the XML element. So you can not use it for the index number by default.

Categories