Extract data from XML with file_get_contents to php [duplicate] - php

This question already has answers here:
Simple XML - Dealing With Colons In Nodes
(4 answers)
Closed 8 years ago.
I'm sending a request to an API which sends a response in XML format.
The response looks like this:
<ns2:HotelInformationResponse hotelId="263933">
<customerSessionId>0ABAAA85-112B-0914-9322-CA866D907EF8</customerSessionId>
<HotelSummary order="0">
<hotelId>263933</hotelId>
<name>Nova Platinum Hotel</name>
<address1>562 Moo 10 Pratamnak Road, Nongprue</address1>
<address2>Banglamung</address2>
<city>Pattaya</city>
<postalCode>20260</postalCode>
<countryCode>TH</countryCode>
How can I get this data so I can print individual values like hotelId or name ?
I have tried like this:
$Geosearch = 'APICALLURLHERE';
$fileContents = file_get_contents($Geosearch);
$string_data = $fileContents;
$xml = simplexml_load_string($string_data);
$hotel_id = (string) $xml->hotelId;
$hotel_name = (string) $xml->name;
echo $hotel_id.' '.$hotel_name;
Also I have tried this:
$xml = simplexml_load_file("APICALLURLHERE");
echo $xml->hotelId;
echo $xml->name;

Acces to remote files
The fopen family of functions, including file_get_contents and simplexml_load_file) by default do no allow access to remote files. This is a very good thing and should stay that way.
If you want to enable remote files, you have to change your PHP configuration.
http://php.net/manual/en/features.remote-files.php
allow_url_fopen = 1
Errors in a XML file
If your file loading is working but you STILL do not get your XML working, you may have a broken XML file. You can debug these issues with this snippet:
$doc = simplexml_load_string($xmlstr);
if (!$doc) {
$errors = libxml_get_errors();
foreach ($errors as $error) {
echo display_xml_error($error, $xml);
}
libxml_clear_errors();
}
http://php.net/manual/de/function.libxml-get-errors.php

Related

Use PHP to parse INI file and run JSON_DECODE

I'm looking to get some input on how to make modular code that performs a PHP parse_ini_file and then uses the returned values to run JSON decodes.
I have a BACnet API that returns a JSON structure for BACnet points in an automation system. I wrote the following code to decode the JSON data to return just the "present-value" field and then I display the value on a webpage.
<?php
$url = "http://hostname.lcl:47800/api/v1/bacnet/devices/10100/objects/0.0";
$json = file_get_contents($url);
$json_data = json_decode($json, true);
echo "<b>Room temperature</b>: ". $json_data["present-value"]. " DEG F";
;?>
This works well but I want to make this code modular so it can be used for many other points.
I created an INI file with a list of other points and the URL that contains the JSON data from the API.
## BACnet Configuration File
# BACnet Object URLs from WACNET Browser API
[bacnet]
SEA_RMT = http://hostname.lcl:47800/api/v1/bacnet/devices/10100/objects/0.0
SEA_SRV_SEA_SV1_01_EXHT = http://hostname.lcl:47800/api/v1/bacnet/devices/10100/objects/0.3
SEA_SRV_SEA_SV1_02_EXHT = http://hostname.lcl:47800/api/v1/bacnet/devices/10100/objects/0.4
SEA_SRV_SEA_SV1_03_EXHT = http://hostname.lcl:47800/api/v1/bacnet/devices/10100/objects/0.5
What I'd like to do is use the INI file to get the present value of each point in the list and then create a variable that is the name of the point and set it equal to the "present-value" field. Then I can reference the point using the PHP variable on the HTML page like this:
<?php echo "$SEA_SRV_SEA_SV1_01_EXHT";?>
I started with the code below but it doesn't work.
<?php
// Parse the settings file
$bacnetini = parse_ini_file('/var/www/config/bacnet.ini');
// Parse the keys to variables and add data
foreach ($bacnetini as $key => $value) {
$url = $value;
$json = file_get_contents($url);
$json_data = json_decode($json, true);
$$key = $json_data;
}
?>
I'd love to get some other opinions on the best way to accomplish this since I don't really know where to go from here.
I've looked through these other Stack Overflow questions but I don't know how to get the pieces to all fit together.
Parsing a config file in php to variables
Get JSON object from URL
Why not try something like this instead? This will allow you to create other sections in your INI file that won't affect your script.
<?php
$bacnetini = parse_ini_file('/var/www/config/bacnet.ini', true);
$data = array();
foreach ($bacnetini['bacnet'] as $key => $url) {
$data[$key] = json_decode(file_get_contents($url), true);
}
var_dump($data['SEA_SRV_SEA_SV1_01_EXHT']);
?>

Parsing NWS XML Data - cap:geocode

I followed the info here:
Parse XML namespaces with php SimpleXML
And that works for everything except the information contained in the "cap:geocode" and "cap:parameter" entries.
$geocode = $entry->children('cap',true)->geocode;
returns an empty value.
Any ideas on how to get at the data inside of the cap:geocode and cap:parameter entries?
<cap:geocode>
<valueName>FIPS6</valueName>
<value>048017 048079</value>
<valueName>UGC</valueName>
<value>TXZ027 TXZ033</value>
</cap:geocode>
I need to read the ValueName/Value pairs.
I used this example here: https://github.com/tylerlane/php.news-leader.com/blob/master/weather/alerts.php
And simplified it for my purposes to get this (echo.php just prints the data out):
$dataFileName = "wx/CAP.xml";
//load the feed
$capXML = simplexml_load_file($dataFileName);
//how many items
$itemsTotal = count($capXML->entry);
if(count($itemsTotal)):
$capXML->registerXPathNamespace('prefix', 'http://www.w3.org/2005/Atom');
$result = $capXML->xpath("//prefix:entry");
foreach($result as $capXML):
$dc = $capXML->children('urn:oasis:names:tc:emergency:cap:1.1');
$event = $dc->event;
$effective = $dc->effective;
$expires = $dc->expires;
$status = $dc->status;
$msgType = $dc->msgType;
$category = $dc->category;
$urgency = $dc->urgency;
$severity = $dc->severity;
$certainty = $dc->certainty;
$areadesc = $dc->areaDesc;
$geopolygon = $dc->polygon;
//get the children of the geocode element
$geocodechildren = $dc->geocode->children();
//only interested in FIPS6 for now
//no guarantee that FIPS6 will be the first child so we have to deal with that
if($geocodechildren->valueName == "FIPS6"){
//isolate all the FIPS codes
$fips = explode( " ", $geocodechildren->value );
} else {
//hide everything else so we don't fail
$fips = Array();
}
//get the VTEC
$parameter_children = $dc->parameter->children();
if($parameter_children->valueName == "VTEC"){
//isolate all VTEC codes
$vtec = explode( ".", $parameter_children->value );
} else {
//hide anything else that may show up
$vtec = Array();
}
include('echo.php');
print_r($fips);
echo "<br/>";
print_r($vtec);
echo "<hr/>";
endforeach;
endif;
Any ideas on how to get at the data inside of the cap:geocode and cap:parameter entries?
The key point in your case is, that the XML provided in the other question is invalid.
You would have noticed that if you had followed a good practice in PHP development: Enable reporting of errors, warning and notices to the highest level, display those as well as log those to file. Then track those warnings.
In your case you should have seen some message like:
Warning: simplexml...: namespace error : Namespace prefix cap on event is not defined in /path/to/script.php on line 42
This is a notice that the cap XML namespace prefix is undefined. That means that SimpleXML will drop it. Those elements are then put into the default namespace of the document so that you can access them directly.
So first of all make yourself comfortable with setting your php.ini file on your development system for development error reporting so that you'll be noticed about unexpected input values. One stop for that is the following question:
How to get useful error messages in PHP?
Next to that you need to decide why the input is wrong and how you'd like to deal with errors. Should it fail (the design of XML suggest to go with the fail route which is also considered a design issue for XML) or do you want to "repair" the XML or do you want to work with the invalid XML. That decision is up to. SimpleXML does work as announced, it's just in your case you got the error unnoticed and you're not doing any error handling so far.
The same problem with similar XML has been asked/answered about previously:
SimpleXML PHP Parsing [Duplicate] (marked as duplicate, albeit the duplicate does not talk about the error)
Create a WS-Security header using SimpleXML? ( create an XML document with SimpleXML with namespace prefixes)

Read an XML style file using PHP

I am trying to retrieve data from this XML style file :
<Product_Group>
<Product_Group_ID>131</Product_Group_ID>
<Product_Group_Title>Thanks for the Memories</Product_Group_Title>
<Products>
<On_Sale_Date>03/01/12 00:00:00.000</On_Sale_Date>
<ISBN>9780007233694</ISBN>
<Title>Thanks for the Memories</Title>
<Format>Paperback</Format>
<Sub_Format/>
<CoverImageURL_Small>http://www.harpercollins.com/harperimages/isbn/small/4/9780007233694.jpg</CoverImageURL_Small>
</Products>
</Product_Group>
I am using following code but this seems to retrieve nothing. any help in fixing this issues would be highly appreciated
$xml = simplexml_load_string($response);
//$xml= $response;
$updates = array();
//loop through all the entry(s) in the feed
for ($i=0; $i<count($xml->Product_Group); $i++)
{
//get the id from entry
$ISBN = $xml->entry[$i]->ISBN;
//get the account link
$Title = $xml->entry[$i]->Title;
//get the tweet
$Product_Group_SEO_Copy = $xml->entry[$i]->Product_Group_SEO_Copy;
}
1) It is not valid XML. What warnings do you see? You'll need to fix them for simplexml_load_string to work properly.
For example, </CoverImageURL_Small> should be <CoverImageURL_Small/>
2) Assuming that Product_Group is not your actual document root (if it is than $xml points to it already and $xml->Product_Group will not work) then you can access each element like
$xml->Product_Group->Products[$i]->ISBN;
3) It's usually simpler to use a foreach loop than a for loop when dealing with simplexml
foreach($xml->Product_Group->Products as $p)
{
$ISBN = $p->ISBN;
//var_dump($ISBN);
}

Parsing XML with PHP (simplexml)

Firstly, may I point out that I am a newcomer to all things PHP so apologies if anything here is unclear and I'm afraid the more layman the response the better. I've been having real trouble parsing an xml file in to php to then populate an HTML table for my website. At the moment, I have been able to get the full xml feed in to a string which I can then echo and view and all seems well. I then thought I would be able to use simplexml to pick out specific elements and print their content but have been unable to do this.
The xml feed will be constantly changing (structure remaining the same) and is in compressed format. From various sources I've identified the following commands to get my feed in to the right format within a string although I am still unable to print specific elements. I've tried every combination without any luck and suspect I may be barking up the wrong tree. Could someone please point me in the right direction?!
$file = fopen("compress.zlib://$url", 'r');
$xmlstr = file_get_contents($url);
$xml = new SimpleXMLElement($url,null,true);
foreach($xml as $name) {
echo "{$name->awCat}\r\n";
}
Many, many thanks in advance,
Chris
PS The actual feed
Since no one followed my closevote, I think I can just as well put my own comments as an answer:
First of all, SimpleXml can load URIs directly and it can do so with stream wrappers, so your three calls in the beginning can be shortened to (note that you are not using $file at all)
$merchantProductFeed = new SimpleXMLElement("compress.zlib://$url", null, TRUE);
To get the values you can either use the implicit SimpleXml API and drill down to the wanted elements (like shown multiple times elsewhere on the site):
foreach ($merchantProductFeed->merchant->prod as $prod) {
echo $prod->cat->awCat , PHP_EOL;
}
or you can use an XPath query to get at the wanted elements directly
$xml = new SimpleXMLElement("compress.zlib://$url", null, TRUE);
foreach ($xml->xpath('/merchantProductFeed/merchant/prod/cat/awCat') as $awCat) {
echo $awCat, PHP_EOL;
}
Live Demo
Note that fetching all $awCat elements from the source XML is rather pointless though, because all of them have "Bodycare & Fitness" for value. Of course you can also mix XPath and the implict API and just fetch the prod elements and then drill down to the various children of them.
Using XPath should be somewhat faster than iterating over the SimpleXmlElement object graph. Though it should be noted that the difference is in an neglectable area (read 0.000x vs 0.000y) for your feed. Still, if you plan to do more XML work, it pays off to familiarize yourself with XPath, because it's quite powerful. Think of it as SQL for XML.
For additional examples see
A simple program to CRUD node and node values of xml file and
PHP Manual - SimpleXml Basic Examples
Try this...
$url = "http://datafeed.api.productserve.com/datafeed/download/apikey/58bc4442611e03a13eca07d83607f851/cid/97,98,142,144,146,129,595,539,147,149,613,626,135,163,168,159,169,161,167,170,137,171,548,174,183,178,179,175,172,623,139,614,189,194,141,205,198,206,203,208,199,204,201,61,62,72,73,71,74,75,76,77,78,79,63,80,82,64,83,84,85,65,86,87,88,90,89,91,67,92,94,33,54,53,57,58,52,603,60,56,66,128,130,133,212,207,209,210,211,68,69,213,216,217,218,219,220,221,223,70,224,225,226,227,228,229,4,5,10,11,537,13,19,15,14,18,6,551,20,21,22,23,24,25,26,7,30,29,32,619,34,8,35,618,40,38,42,43,9,45,46,651,47,49,50,634,230,231,538,235,550,240,239,241,556,245,244,242,521,576,575,577,579,281,283,554,285,555,303,304,286,282,287,288,173,193,637,639,640,642,643,644,641,650,177,379,648,181,645,384,387,646,598,611,391,393,647,395,631,602,570,600,405,187,411,412,413,414,415,416,649,418,419,420,99,100,101,107,110,111,113,114,115,116,118,121,122,127,581,624,123,594,125,421,604,599,422,530,434,532,428,474,475,476,477,423,608,437,438,440,441,442,444,446,447,607,424,451,448,453,449,452,450,425,455,457,459,460,456,458,426,616,463,464,465,466,467,427,625,597,473,469,617,470,429,430,615,483,484,485,487,488,529,596,431,432,489,490,361,633,362,366,367,368,371,369,363,372,373,374,377,375,536,535,364,378,380,381,365,383,385,386,390,392,394,396,397,399,402,404,406,407,540,542,544,546,547,246,558,247,252,559,255,248,256,265,259,632,260,261,262,557,249,266,267,268,269,612,251,277,250,272,270,271,273,561,560,347,348,354,350,352,349,355,356,357,358,359,360,586,590,592,588,591,589,328,629,330,338,493,635,495,507,563,564,567,569,568/mid/2891/columns/merchant_id,merchant_name,aw_product_id,merchant_product_id,product_name,description,category_id,category_name,merchant_category,aw_deep_link,aw_image_url,search_price,delivery_cost,merchant_deep_link,merchant_image_url/format/xml/compression/gzip/";
$zd = gzopen($url, "r");
$data = gzread($zd, 1000000);
gzclose($zd);
if ($data !== false) {
$xml = simplexml_load_string($data);
foreach ($xml->merchant->prod as $pr) {
echo $pr->cat->awCat . "<br>";
}
}
<?php
$xmlstr = file_get_contents("compress.zlib://$url");
$xml = simplexml_load_string($xmlstr);
// you can transverse the xml tree however you want
foreach ($xml->merchant->prod as $line) {
// $line->cat->awCat -> you can use this
}
more information here
Use print_r($xml) to see the structure of the parsed XML feed.
Then it becomes obvious how you would traverse it:
foreach ($xml->merchant->prod as $prod) {
print $prod->pId;
print $prod->text->name;
print $prod->cat->awCat; # <-- which is what you wanted
print $prod->price->buynow;
}
$url = 'you url here';
$f = gzopen ($url, 'r');
$xml = new SimpleXMLElement (fread ($f, 1000000));
foreach($xml->xpath ('//prod') as $name)
{
echo (string) $name->cat->awCatId, "\r\n";
}

How extract XML data using PHP

I am required to extract the information at a particular "area" of this large collection of xml. But i'm not familiar with extracting xml. I've looked through the site and tried various ways but all i get back is "Error in my_thread_global_end(): 1 threads didn't exit"
Here's the url of the xml i'm getting my data from:
ftp://ftp2.bom.gov.au/anon/gen/fwo/IDV10753.xml
I would to retrieve all the 7 forecast-period located only for the Swans Hill "area".
Help please
I agree that using php's simple xml parser is the way to go with this one.
You can make your life easy here using the xpath method of extracting data from the xml.
There's an xpath tutorial here: http://www.w3schools.com/xpath/
And php documentation for it here: http://www.php.net/manual/en/simplexmlelement.xpath.php
Try this out
<?php
/*
Get the file with CURL
$curl_handle=curl_init();
curl_setopt($curl_handle,CURLOPT_URL,'ftp://ftp2.bom.gov.au/anon/gen/fwo/IDV10753.xml');
curl_setopt($curl_handle,CURLOPT_RETURNTRANSFER,true);
$xml_data = curl_exec($curl_handle);
curl_close($curl_handle);
*/
/*
Open the file locally
*/
$xml_data = file_get_contents("weather.xml");
$xml = simplexml_load_string($xml_data);
$result = $xml->xpath("//area[#description='Swan Hill']/forecast-period");
date_default_timezone_set('America/New_York');
foreach ($result as $day) {
//print_r($day);
$day_of_the_week = date("l", strtotime($day["start-time-local"])); //start-time-local is an attribute of a result, so use the [] syntax
$forecast = $day->text; //text is a child node, so use the -> syntax
printf("%s: %s\n", $day_of_the_week, $forecast);
}
?>
EDIT More illustrative example

Categories