Parsing NWS XML Data - cap:geocode - php

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)

Related

simple_xml_load_string if nothing is returned

I am attempting to only run a loop if xml results actually exist. I am getting the xml results via:
$albums = simplexml_load_string(curl_get($api_url . '/videos.xml'));
What I want to be able to do is that on the next line say:
if($albums = hasAValue())
// Loop
Any ideas? Or a way to check before I load the XML data?
Side note: This is using the Vimeo API.
No, you need to further go down with the resultant with the namespace, reach till body give the xpath and work on.
$albums->registerXPathNamespace('soap', 'http://schemas.xmlsoap.org/soap/envelope/');
To be specific, let me know the XML response you are getting i will let you the output.
UPDATED
$albums = simplexml_load_string("#your response#");
echo count($xml->children());
The dirty way:
$albums = #simplexml_load_string(curl_get($api_url . '/videos.xml'));
if ($albums)
{
...
}
This is dirty because of the Error Control Operator # which is used to "deal" with the error cases (e.g. problem fetching the remote location).
The alternative is to differentiate more here:
$xml = curl_get($api_url . '/videos.xml');
$albums = NULL;
if ($xml)
{
$albums = simplexml_load_string($xml);
}
if ($albums)
{
...
}

Use PHP to load XML Data into Oracle

I'm fairly new to php although I've been programming for a couple years.
I'm working on a project and the end goal is to load certain elements of an xml file into an oracle table on a nightly basis. I have a script which runs nightly and saves a the file on my local machine. I've searched endlessly for answers but have been unsuccessful.
Here is an aggregated example of the xml file.
<?xml version="1.0" encoding="UTF-8" ?>
<Report account="7869" start_time="2012-02-23T00:00:00+00:00" end_time="2012-02-23T15:27:59+00:00" user="twilson" more_sessions="false">
<Session id="ID742247692" realTimeID="4306650378">
<Visitor id="5390643113837">
<ip>128.XXX.XX.XX</ip>
<agent>MSIE 8.0</agent>
</Visitor>
</Session>
<Session id="ID742247695" realTimeID="4306650379">
<Visitor id="7110455516320">
<ip>173.XX.XX.XXX</ip>
<agent>Chrome 17.0.963.56</agent>
</Visitor>
</Session>
</Report>
One thing to note is that the xml file will contain several objects which I will need to load into my table and the above example would just be for two rows of data. I'm familiar with the whole process of connecting and loading data into oracle and have setup similar scripts which perform ETL of txt. and csv. files using php. Unfortunately for me in this case the data is stored in xml. The approach I've taken when loading a csv. file is to load the data into an array and proceed from there.
I'm pretty certain that I can use something similar and perhaps create variable for each or something similar but am not really too sure how to do that with an xml. file.
$xml = simplexml_load_file('C:/Dev/report.xml');
echo $xml->Report->Session->Visitor->agent;
In the above code i'm trying to just return the agent associated with each visitor. This returns an error 'Trying to get property of non-object in C:\PHP\chatTest.php on line 11'
The end result would be for me to load the data into a table similar to the example I provided would be to load two rows into my table which would look similar to below however I think I can handle that if i'm able to get the data into an array or something similar.
IP|AGENT
128.XXX.XX.XX MSIE 8.0
173.XX.XX.XXX Chrome 17.0.963.56
Any help would be greatly appreciated.
Revised Code:
$doc = new DOMDocument();
$doc->load( 'C:/Dev/report.xml' );
$sessions = $doc->getElementsByTagName( "Session" );
foreach( $sessions as $session )
{
$visitors = $session->getElementsByTagName( "Visitor" );
foreach( $visitors as $visitor )
$sessionid = $session->getAttribute( 'realTimeID' );
{
$ips = $visitor->getElementsByTagName( "ip" );
$ip = $ips->item(0)->nodeValue;
$agents = $visitor->getElementsByTagName( "agent" );
$agent = $ips->item(0)->nodeValue;
echo "$sessionid- $ip- $agent\n";
}}
?>
The -> operator in PHP means that you are trying to invoke a field or method on an object. Since Report is not a method within $xml, you are receiving the error that you are trying to invoke a property on a non-object.
You can try something like this (don't know if it works, didn't test it and haven't written PHP for a long time, but you can google it):
$doc = new DOMDocument();
$doc->loadXML($content);
foreach ($doc->getElementsByTagName('Session') as $node)
{
$agent = $node->getElementsByTagName('Visitor')->item(0)->getElementsByTagName('agent')->item(0)->nodeValue;
}
edit:
Adding stuff to an array in PHP is easy as this:
$arr = array();
$arr[] = "some data";
$arr[] = "some more data";
The PHP arrays should be seen as a list, since they can be resized on the fly.
I was able to figure this out using simplexml_load_file rather than the DOM approach. Although DOM works after modifying the Leon's suggestion the approach below is what I would suggest.
$xml_object = simplexml_load_file('C:/Dev/report.xml');
foreach($xml_object->Session as $session) {
foreach($session->Visitor as $visitor) {
$ip = $visitor->ip;
$agent = $visitor->agent;
}
echo $ip.','.$agent."\n";
}

Error reading from webservices xml file

I'm trying to get a product list for a client from a webservices xml file over to an SQL databse using a little php script, but I can't seem to get it to work.
The relevant code is as follows:
$c = 0;
...
$xml = simplexml_load_file($completeurl);
$listingsArray = $xml->listings->listing;
foreach($listingsArray as $listing){
$addition[0] = $listing[$c]->type;
$addition[1] = $listing[$c]->condition;
//etcetera
c = c + 1;
}
The XML file is formated like:
<inventory>
<listings>
<listing>
//tags for type, condition, etc
</listing>
</listings>
</inventory>
$completeurl is a string that contains the url of the xml file
$addition is an array that's defined earlier in the code
I've been working on this for a while now, but I can't seem to figure out where the error is in my code. The problem that I'm having is that $listingsArray should have close to 100 elements in it, but is constantly coming up with 0. Anybody see what I'm doing wrong?
EDIT: I tried changing
$listingsArray = $xml->listings->listing;
to
$listingsArray = $xml->listings;
But empty strings are still being written to the $addition array. A var_dump of listingsArray show that all of the information is in there, though.
If you want each listing i believe you are going a child too deep.
$listingsArray = $xml->listings->listing;
shoudl be
$listingsArray = $xml->listings;
foreach($listingsArray as $listing){
$addition[0] = $listing[$c]->type;
$addition[1] = $listing[$c]->condition;
//etcetera
}
additionally what is $c? It also helps to post your exact errors. When debugging inserting var_dumps is extremely helpful. If you think the probelm is $listingArray
print it out and see if it contains the data you want.
changing
$listingsArray = $xml->listings->listing;
to
$listingsArray = $xml->listings;
Should solve the issue. You set $listingsArray to the array of listings, then the foreach tries to go down another level

PHP Parse XML with Attributes

I have an XML document that I am trying to get some of the values for and don't know how to get to the attributes. An example of the structure and values are below:
<vin_number value="3N1AB51D84L729887">
<common_data>
<engines>
</engines>
</common_data>
<available_vehicle_styles>
<vehicle_style name="SE-R 4dr Sedan" style_id="100285116" complete="Y">
<engines>
<engine brand="" name="ED 2L NA I 4 double overhead cam (DOHC) 16V"></engine>
</engines>
</vehicle_style>
</available_vehicle_styles>
</vin_number>
I am trying to get the engine["name"] attribute (NOT "ENGINES"). I thought the following would work but I get errors (I cant parse past "vehicle_style")
$xml = simplexml_load_file($fileVIN);
foreach($xml->vin_number->available_vehicle_styles->vehicle_style->engines->engine->attributes() as $a => $b) {
echo $b;
}
Assuming your XML is structured in the same was as this example XML, the following two snippets will get the engine name.
The property hierarchy way (split onto multiple lines so you can read it).
$name = (string) $xml->vin_number
->available_vehicle_styles
->vehicle_style
->engines
->engine['name'];
Or the more concise XPath way.
$engines = $xml->xpath('//engines/engine');
$name = (string) $engines[0]['name'];
Unless there are multiple engine names in your XML, there is no need to use a foreach loop at all.
(See both snippets running on a codepad.)
Use the SimpleXMLElement::attributes method to get the attributes:
foreach($xml->available_vehicle_styles->vehicle_style as $b) {
$attrs = $b->attributes();
echo "Name = $attrs->name";
}
Note: I slightly changed the "path" to the element starting from $xml because that's how it loaded the fragment for me.
By this layout, there could be more than one engine per engines block, so you have to explicitly pick the first one. (Assuming you know for sure there's only going to be one.)
$name = $xml->available_vehicle_styles->vehicle_style->engines->engine[0]->attributes()->name;

PHP json_encode a debug_backtrace() with resource types

Currently, I have a logger which logs errors together with a backtrace.
The logger serializes the backtrace to JSON via json_encode().
Let's look at some hypothetical code...
<?php
error_reporting(-1); // show all errors
function test($b){
echo json_encode(debug_backtrace()); // take a backtrace snapshot
}
$c = imagecreate(50,50); // create a resource...
test($c); // ...and pass to function
?>
If you run the code above, we will see something like:
Warning: json_encode() [function.json-encode]: type is unsupported, encoded as null in /code/ch6gVw on line 5
[{"file":"/code/ch6gVw","line":8,"function":"test","args":[null]}]
We can notice two things going on here:
The logger itself is causing a warning! Bad bad bad!
The logged data tells us we passed a null to the function?!?!
So, my proposed solution is something like:
foreach($trace as $i=>$v)
if(is_resource($v))
$trace[$i] = (string)$v.' ('.get_resource_type($v).')';
The result would look like Resource id #1 (gd)
This, however, may cause some grave issues.
We need to somehow track which arrays we looped through so as to avoid ending up in infinite loops with arrays referencing themselves ($GLOBALS tend to cause this mess).
We would also have to convert resources of object properties, but objects, unlike arrays, are not a copy of the original thing, hence changing the property changes the live object. On the other hand, how safe is it to clone() the object?
Won't such a loop severely slow down the server (backtraces tend to be large, no)?
I ended up with the following function:
function clean_trace($branch){
if(is_object($branch)){
// object
$props = array();
$branch = clone($branch); // doesn't clone cause some issues?
foreach($props as $k=>$v)
$branch->$k = clean_trace($v);
}elseif(is_array($branch)){
// array
foreach($branch as $k=>$v)
$branch[$k] = clean_trace($v);
}elseif(is_resource($branch)){
// resource
$branch = (string)$branch.' ('.get_resource_type($branch).')';
}elseif(is_string($branch)){
// string (ensure it is UTF-8, see: https://bugs.php.net/bug.php?id=47130)
$branch = utf8_encode($branch);
}
// other (hopefully serializable) stuff
return $branch;
}
You can see it in action here. However, I'm not convinced:
It is quite slow (iterating over lots of data)
It is quite memory intensive (data needs to be copied to not mess the original)
It is not safe in case where arrays/objects reference themselves
Example: $a = array(); $a['ref'] = &$a; (PHP does this to some internal variables)
I'm concerned that cloning objects may have some serious side-effects (consider the magic method __clone(), an invitation to wreck havoc).
So you are trying to store the backtrace as a data structure that can be used to pretty-print the results later on?
If that isn't needed I'd just store $result = print_r(debug_backtrace(), true) and be done with it.
If not my first shot would be something like:
<?php
error_reporting(-1);
function test($b){
echo json_encode(clean(debug_backtrace()));
}
$c = fopen("/tmp/foo", "w");
test($c);
function clean($trace) {
array_walk_recursive($trace, function(&$element) {
if(is_object(&$element)) {
// work around unrealizable elements and preserve typing
$element = array(get_class($element), (object)$element);
} else if(is_resource($element)) {
$element = get_resource_type($element) . '#' .(int)$element;
}
});
return $trace;
}
It's just a rough sketch but I'm not aware of any project that stores backtracks for later inspection in a non textual or already processed format and looking around the mature frameworks didn't bring anything up

Categories