I am making a simple web-based application that shows the arrival time (in minutes) of trains arriving at the metro station near my home.
Metro (The subway in Washington, DC) has released an API that allows developers to access this information: http://developer.wmata.com/docs/read/GetRailStationInfo
When I use the example code from the link above I get a list of tagged text like this:
<AIMPredictionResp xmlns="http://www.wmata.com" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Trains>
<AIMPredictionTrainInfo>
<Car>6</Car>
<Destination>NewCrltn</Destination>
<DestinationCode>D13</DestinationCode>
<DestinationName>New Carrollton</DestinationName>
<Group>1</Group>
<Line>OR</Line>
<LocationCode>K03</LocationCode>
<LocationName>Virginia Square</LocationName>
<Min>7</Min>
</AIMPredictionTrainInfo>
</Trains>
All I want to display is the minutes wrapped in the < Min > < /Min > tag. What's the best way to go about this? Is there a PHP script that I can write that will pull just that number? If so, could you please point me in the right direction?
Thanks!
UPDATE:
Thank you all very much. I have tried an example from the tutorial you sent, but when I switch my URL (with key) in it does not display anything.
<?php
$trainInfo = simplexml_load_file("api.wmata.com/StationPrediction.svc/GetPrediction/_KEYXXXXXXXX);
print $trainInfo->AIMPredictionTrainInfo->LocationName;
print $trainInfo->AIMPredictionTrainInfo->Min;
?>
The direction you want to be pulled in is SimpleXML
EXAMPLE (not tested):
<?php
$xml = new SimpleXMLElement($my_input_xml);
echo $xml->getMin() . "<br>";
?>
Here are some other good tutorials:
http://php.net/manual/en/simplexml.examples-basic.php
http://www.phpeveryday.com/articles/PHP-SimpleXML-Tutorial-P846.html
'Hope that helps!
Here comes an example using DOMXPath (tested). Important is to register the default namespace:
$data = <<<EOF
<?xml version="1.0"?>
<AIMPredictionResp xmlns="http://www.wmata.com" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Trains>
<AIMPredictionTrainInfo>
<Car>6</Car>
<Destination>NewCrltn</Destination>
<DestinationCode>D13</DestinationCode>
<DestinationName>New Carrollton</DestinationName>
<Group>1</Group>
<Line>OR</Line>
<LocationCode>K03</LocationCode>
<LocationName>Virginia Square</LocationName>
<Min>7</Min>
</AIMPredictionTrainInfo>
</Trains>
</AIMPredictionResp>
EOF;
$doc = new DOMDocument();
$doc->loadXML($data);
$selector = new DOMXPath($doc);
$selector->registerNamespace(
'default',
'http://www.wmata.com'
);
$query = '//default:Min';
foreach($selector->query($query) as $node) {
var_dump($node->nodeValue);
}
Output:
string(1) "7"
Related
I have a page in php where I have to parse an xml.
I have done this for example:
$hotelNodes = $xml_data->getElementsByTagName('Hotel');
foreach($hotelNodes as $hotel){
$supplementsNodes2 = $hotel->getElementsByTagName('BoardBase');
foreach($supplementsNodes2 as $suppl2) {
echo'<p>HERE</p>'; //not enter here
}
}
}
In this code I access to each hotel of my xml, and foreach hotel I would like to search the tag BoardBase but it doesn0t enter inside it.
This is my xml (cutted of many parts!!!!!)
<hotel desc="DESC" name="Hotel">
<selctedsupplements>
<boardbases>
<boardbase bbpublishprice="0" bbprice="0" bbname="Colazione Continentale" bbid="1"></boardbase>
</boardbases>
</selctedsupplements>
</occupancy></occupancies>
</hotel>
I have many nodes that doesn't have BoardBase but sometimes there is but not enter.
Is possible that this node isn't accessible?
This xml is received by a server with a SoapClient.
If I inspect the XML printed in firebug I can see the node with opacity like this:
I have also tried this:
$supplementsNodes2 = $hotel->getElementsByTagName('boardbase');
but without success
2 issues I can see from the get-go: XML names are case-sensitive, hence:
$hotelNodes = $xml_data->getElementsByTagName('Hotel');
Can't work, because your xml node looks like:
<hotel desc="DESC" name="Hotel">
hotel => lower-case!
As you can see here:
[...] names for such things as elements, while XML is explicitly case sensitive.
The official specs specify tag names as case-sensitive, so getElementsByTagName('FOO') won't return the same elements as getElementsByTagName('foo')...
Secondly, you seem to have some tag-soup going on:
</occupancy></occupancies>
<!-- tag names don't match, both are closing tags -->
This is just plain invalid markup, it should read either:
<occupancy></occupancy>
or
<occupancies></occupancies>
That would be the first 2 ports of call.
I've set up a quick codepad using this code, which you can see here:
$xml = '<hotel desc="DESC" name="Hotel">
<selctedsupplements>
<boardbases>
<boardbase bbpublishprice="0" bbprice="0" bbname="Colazione Continentale" bbid="1"></boardbase>
</boardbases>
</selctedsupplements>
<occupancy></occupancy>
</hotel>';
$dom = new DOMDocument;
$dom->loadXML($xml);
$badList = $dom->getElementsByTagName('Hotel');
$correctList = $dom->getElementsByTagName('hotel');
echo sprintf("%d",$badList->lenght),
' compared to ',
$correctList->length, PHP_EOL;
The output was "0 compared to 1", meaning that using a lower-case selector returned 1 element, the one with the upper-case H returned an empty list.
To get to the boardbase tags for each hotel tag, you just have to write this:
$hotels = $dom->getElementsByTagName('html');
foreach($hotels as $hotel)
{
$supplementsNodes2 = $hotel->getElementsByTagName('boardbase');
foreach($supplementsNodes2 as $node)
{
var_dump($node);//you _will_ get here now
}
}
As you can see on this updated codepad.
Alessandro, your XML is a mess (=un casino), you really need to get that straight. Elias' answer pointed out some very basic stuff to consider.
I built on the code pad Elias has been setting up, it is working perfectly with me:
$dom = new DOMDocument;
$dom->loadXML($xml);
$hotels = $dom->getElementsByTagName('hotel');
foreach ($hotels as $hotel) {
$bbs = $hotel->getElementsByTagName('boardbase');
foreach ($bbs as $bb) echo $bb->getAttribute('bbname');
}
see http://codepad.org/I6oxkEOC
I am trying to edit an XML document using PHP, but it does not seem to work, wonder where I am going wrong!
I am trying to do it the following way which I found online. Can anyone please show me the right direction?
XML
<gold>
<coin>
<id>1</id>
<title>Gold Coin 50 Grams</title>
<price>500 rupees</price>
<dimension>20 MM</dimension>
<thumb_url>http://animsinc.com/Expertise-media.png</thumb_url>
</coin>
<coin>
<id>2</id>
<title>Gold Coin 50 Grams</title>
<price>500 rupees</price>
<dimension>20 MM</dimension>
<thumb_url>
http://animsinc.com/Expertise-media.png</thumb_url>
</coin>
...
</gold>
PHP Code
$xmlDoc = new DOMDocument();
$xmlDoc->loadXml($str);
$events = $xmlDoc->getElementsByTagName("coin");
foreach($events as $event){
$eventNames = $event->getElementsByTagName("price");
$eventN = $eventNames->item(0)->nodeValue;
if('500' == $eventN){ // ** Failed here, instead of '500',
// I used '500 rupees' and it worked, as that matches the original text.**
$eventNames->item(0)->nodeValue = $rest ;
}
}
var_dump($xmlDoc->saveXML());
Here's the desired result. Notice how I've changed the price tag to 2495 rupees.
<gold>
<coin>
<id>1</id>
<title>Gold Coin 50 Grams</title>
<price>2495 rupees</price>
<dimension>20 MM</dimension>
<thumb_url>http://animsinc.com/Expertise-media.png</thumb_url>
</coin>
<coin>
<id>2</id>
<title>Gold Coin 50 Grams</title>
<price>2495 rupees</price>
<dimension>20 MM</dimension>
<thumb_url>
http://animsinc.com/Expertise-media.png</thumb_url>
</coin>
...
</gold>
Here's a quick solution based on SimpleXML:
$gold = new SimpleXMLElement($xml);
foreach ($gold->coin as $coin) {
$coin->price = str_replace('500', $rest, (string) $coin->price);
}
echo $gold->asXML();
Working demo.
Oh hey, it was a mistake on my part, the function works perfect, instead of
if('500' == $eventN)
The Correct woulld be
if('500 rupees' == $eventN)
Dumb
I've been searching for a while on this and haven't had much luck. I've found plenty of resources showing how to echo data from dynamic XML, but I'm a PHP novice, and nothing I've written seems to grab and print exactly what I want, though from everything I've heard, it should be relatively easy. The source XML (located at 192.168.0.15:8080/requests/status.xml) is as follows:
<root>
<fullscreen>0</fullscreen>
<volume>97</volume>
<repeat>false</repeat>
<version>2.0.5 Twoflower</version>
<random>true</random>
<audiodelay>0</audiodelay>
<apiversion>3</apiversion>
<videoeffects>
<hue>0</hue>
<saturation>1</saturation>
<contrast>1</contrast>
<brightness>1</brightness>
<gamma>1</gamma>
</videoeffects>
<state>playing</state>
<loop>true</loop>
<time>37</time>
<position>0.22050105035305</position>
<rate>1</rate>
<length>168</length>
<subtitledelay>0</subtitledelay>
<equalizer/>
<information>
<category name="meta">
<info name="description">
000003EC 00000253 00000D98 000007C0 00009C57 00004E37 000068EB 00003DC5 00015F90 00011187
</info>
<info name="date">2003</info>
<info name="artwork_url"> file://brentonshp04/music%24/Music/Hackett%2C%20Steve/Guitar%20Noir%20%26%20There%20Are%20Many%20Sides%20to%20the%20Night%20Disc%202/Folder.jpg
</info>
<info name="artist">Steve Hackett</info>
<info name="publisher">Recall</info>
<info name="album">Guitar Noir & There Are Many Sides to the Night Disc 2
</info>
<info name="track_number">5</info>
<info name="title">Beja Flor [Live]</info>
<info name="genre">Rock</info>
<info name="filename">Beja Flor [Live]</info>
</category>
<category name="Stream 0">
<info name="Bitrate">128 kb/s</info>
<info name="Type">Audio</info>
<info name="Channels">Stereo</info>
<info name="Sample rate">44100 Hz</info>
<info name="Codec">MPEG Audio layer 1/2/3 (mpga)</info>
</category>
</information>
<stats>
<lostabuffers>0</lostabuffers>
<readpackets>568</readpackets>
<lostpictures>0</lostpictures>
<demuxreadbytes>580544</demuxreadbytes>
<demuxbitrate>0.015997290611267</demuxbitrate>
<playedabuffers>0</playedabuffers>
<demuxcorrupted>0</demuxcorrupted>
<sendbitrate>0</sendbitrate>
<sentbytes>0</sentbytes>
<displayedpictures>0</displayedpictures>
<demuxreadpackets>0</demuxreadpackets>
<sentpackets>0</sentpackets>
<inputbitrate>0.016695899888873</inputbitrate>
<demuxdiscontinuity>0</demuxdiscontinuity>
<averagedemuxbitrate>0</averagedemuxbitrate>
<decodedvideo>0</decodedvideo>
<averageinputbitrate>0</averageinputbitrate>
<readbytes>581844</readbytes>
<decodedaudio>0</decodedaudio>
</stats>
</root>
What I'm trying to write is a simple PHP script that echoes the artist's name (In this example Steve Hackett). Actually I'd like it to echo the artist, song and album, but I'm confident that if I'm shown how to retrieve one, I can figure out the rest on my own.
What little of my script which actually seems to work goes as follows. I've tried more than what's below, but I left out the bits that I know for a fact aren't working.
<?PHP
$file = file_get_contents('http://192.168.0.15:8080/requests/status.xml');
$sxe = new SimpleXMLElement($file);
foreach($sxe->...
echo "Artist: "...
?>
I think I need to use foreach and echo, but I can't figure out how to do it in a way that will print what's between those info brackets.
I'm sorry if I've left anything out. I'm not only new to PHP, but I'm new to StackOverflow too. I've referenced this site in other projects, and it's always been incredibly helpful, so thanks in advance for your patience and help!
////////Finished Working Script - Thanks to Stefano and all who helped!
<?PHP
$file = file_get_contents('http://192.168.0.15:8080/requests/status.xml');
$sxe = new SimpleXMLElement($file);
$artist_xpath = $sxe->xpath('//info[#name="artist"]');
$album_xpath = $sxe->xpath('//info[#name="album"]');
$title_xpath = $sxe->xpath('//info[#name="title"]');
$artist = (string) $artist_xpath[0];
$album = (string) $album_xpath[0];
$title = (string) $title_xpath[0];
echo "<B>Artist: </B>".$artist."</br>";
echo "<B>Title: </B>".$title."</br>";
echo "<B>Album: </B>".$album."</br>";
?>
Instead of using a for loop, you can obtain the same result with XPath:
// Extraction splitted across two lines for clarity
$artist_xpath = $sxe->xpath('//info[#name="artist"]');
$artist = (string) $artist_xpath[0];
echo $artist;
You will have to adjust the xpath expression (i.e. change #name=... appropriately), but you get the idea. Also notice that [0] is necessary because xpath will return an array of matches (and you only need the first) and the cast (string) is used to extract text contained in the node.
Besides, your XML is invalid and will be rejected by the parser because of the literal & appearing in the <info name="album"> tag.
If you look at your code again, you are missing a function that turns the first result of the xpath expression into a string of a SimpleXMLElement (casting).
One way to write this once is to extend from SimpleXMLElement:
class BetterXMLElement extends SimpleXMLElement
{
public function xpathString($expression) {
list($result) = $this->xpath($expression);
return (string) $result;
}
}
You then create the more specific SimpleXMLElement like you did use the less specific before:
$file = file_get_contents('http://192.168.0.15:8080/requests/status.xml');
$sxe = new BetterXMLElement($file);
And then you benefit in your following code:
$artist = $sxe->xpathString('//info[#name="artist"]');
$album = $sxe->xpathString('//info[#name="album"]');
$title = $sxe->xpathString('//info[#name="title"]');
echo "<B>Artist: </B>".$artist."</br>";
echo "<B>Title: </B>".$title."</br>";
echo "<B>Album: </B>".$album."</br>";
This spares you some repeated code. This means as well less places you can make an error in :)
Sure you can further on optimize this by allowing to pass an array of multiple xpath queries and returning all values named then. But that is something you need to write your own according to your specific needs. So use what you learn in programming to make programming more easy :)
If you want some more suggestions, here is another, very detailed example using DOMDocument, the sister-library of SimpleXML. It is quite advanced but might give you some good inspiration, I think something similar is possible with SimpleXML as well and this is probably what you're looking for in the end:
Extracting data from HTML using PHP and xPath
I am using the ebay trading api to get a sellers stock which is currently listed. I am using the call GetSellerList.I am having trouble parsing the xml which I would then insert into there website shop.
This is the xml request.
<GetSellerListRequest xmlns='urn:ebay:apis:eBLBaseComponents'>
<UserID>".$user_id."</UserID>
<DetailLevel>ReturnAll</DetailLevel>
<ErrorLanguage>RFC 3066</ErrorLanguage>
<WarningLevel>Low</WarningLevel>
<Version>".$compat_level."</Version>
<RequesterCredentials>
<eBayAuthToken>".$auth_token."</eBayAuthToken>
</RequesterCredentials>
<StartTimeFrom>2012-06-12T23:35:27.000Z</StartTimeFrom>
<StartTimeTo>2012-08-30T23:35:27.000Z</StartTimeTo>
<Pagination>
<EntriesPerPage>200</EntriesPerPage>
</Pagination>
<OutputSelector>ItemArray.Item.Title</OutputSelector>
<OutputSelector>ItemArray.Item.Description</OutputSelector>
<OutputSelector>ItemArray.Item.BuyItNowPrice</OutputSelector>
<OutputSelector>ItemArray.Item.Quantity</OutputSelector>
</GetSellerListRequest>
I am not the best with php, I am still learning so i have looked through w3schools and php docs and found nothing. I have been using this (off of ebay tuts) to try and get the values of the xml tags by using getElementsByTagName.
$dom = new DOMDocument();
$dom->loadXML($response);
$titles = $dom->getElementsByTagName('Title')->length > 0 ? $dom->getElementsByTagName('Title')->item(0)->nodeValue : '';
Now i was hoping that i would be able to create an array with this then use foreach to insert them into the db but when i use this it only gets the value of the first 'Title' tag
Im sure there is a way to create an array with all values of 'Title' in it.
All help is appreciated.
This would be easier to answer if you posted the response XML (just the relevant portion) rather than the request.
The code you have will only grab the first item - specifically this part:
$dom->getElementsByTagName('Title')->item(0)->nodeValue
Rather, you'll want to loop through all the Title elements and extract their nodeValue. This is a starting point:
$dom = new DOMDocument();
$dom->loadXML($response);
$title_nodes = $dom->getElementsByTagName('Title');
$titles = array();
foreach ($title_nodes as $node) {
$titles[] = $node->nodeValue;
}
below is the structure of a feed I managed to print the content using this xpath
$xml->xpath('/rss/channel//item')
the structure
<rss><channel><item><pubDate></pubDate><title></title><description></description><link></link><author></author></item></channel></rss>
However some of my files follow this structure
<feed xmlns="http://www.w3.org/2005/Atom" .....><entry><published></published><title></title><description></description><link></link><author></author></entry></feed>
and I guessed that this should be the xpath to get the content of entry
$xml->xpath('/feed//entry')
something that proved me wrong.
My question is what is the right xpath to use? Am i missing something else ?
This is the code
<?php
$feeds = array('http://feeds.feedburner.com/blogspot/wSuKU');
$entries = array();
foreach ($feeds as $feed) {
$xml = simplexml_load_file($feed);
$entries = array_merge($entries, $xml->xpath('/feed//entry'));
}
echo "<pre>"; print_r($entries); echo"</pre>";
?>
try this:
$xml->registerXPathNamespace('f', 'http://www.w3.org/2005/Atom');
$xml->xpath('/f:feed/f:entry');
If you want a single XPath expression that will work when applied to either an RSS or an ATOM feed, you could use either of the following XPath expressions:
This one is the most precise, but also the most verbose:
(/rss/channel/item
| /*[local-name()='feed' and namespace-uri()='http://www.w3.org/2005/Atom']
/*[local-name()='entry' and namespace-uri()='http://www.w3.org/2005/Atom'])
This one ignores the namespace of the ATOM elements and just matches on their local-name():
(/rss/channel/item | /*[local-name()='feed']/*[local-name()='entry'])
This one is the most simple, but the least precise and the least efficient:
/*//*[local-name()='item' or local-name()='entry']