I'm sure there's a pretty obvious solution to this problem...but it's alluding me.
I've got an XML feed that I want to pull information from - from only items with a specific ID. Let lets say we have the following XML:
<XML>
<item>
<name>John</name>
<p:id>1</id>
<p:eye>Blue</eye>
<p:hair>Black</hair>
</item>
<item>
<name>Jake</name>
<p:id>2</id>
<p:eye>Hazel</eye>
<p:hair>White</hair>
</item>
<item>
<name>Amy</name>
<p:id>3</id>
<p:eye>Brown</eye>
<p:hair>Yellow</hair>
</item>
<item>
<name>Tammy</name>
<p:id>4</id>
<p:eye>Blue</eye>
<p:hair>Black</hair>
</item>
<item>
<name>Blake</name>
<p:id>5</id>
<p:eye>Green</eye>
<p:hair>Red</hair>
</item>
</xml>
And I want to pull ONLY people with the ID 3 and 1 into specific spots on a page (there will be no double IDs - unique IDs for each item). Using SimpleXML and a forloop I can easily display each ITEM on a page using PHP - with some "if ($item->{'id'} == #)" statements (where # is the ID I'm looking for(, I can also display the info for each ID I'm looking for.
The problem I'm running into is how to distribute the information across the page. I'm trying to pull the information into specific spots on a page my first attempt at distributing the specific fields across the page aren't working as follows:
<html>
<head><title>.</title></head>
<body>
<?php
(SimpleXML code / For Loop for each element here...)
?>
<H1>Staff Profiles</h1>
<h4>Maintenance</h4>
<p>Maintenance staff does a lot of work! Meet your super maintenance staff:</p>
<?php
if($ID == 1) {
echo "Name:".$name."<br/>";
echo "Eye Color:".$eye."<br/>";
echo "Hair Color:".$hair."<br/>";
?>
<h4>Receptionists</h4>
<p>Always a smiling face - meet them here:</p>
<?php
if($ID == 3) {
echo "Name:".$name."<br/>";
echo "Eye Color:".$eye."<br/>";
echo "Hair Color:".$hair."<br/>";
?>
<H4>The ENd</h4>
<?php (closing the four loop) ?>
</body>
</html>
But it's not working - it randomly starts repeating elements on my page (not even the XML elements). My method is probably pretty...rudimentary; so a point in the right direction is much appreciated. Any advice?
EDIT:
New (NEW) XPATH code:
$count = 0;
foreach ($sxe->xpath('//item') as $item) {
$item->registerXPathNamespace('p', 'http://www.example.com/this');
$id = $item->xpath('//p:id');
echo $id[$count] . "\n";
echo $item->name . "<br />";
$count++;
}
use xpath to accomplish this, and write a small function to retrieve a person by id.
function getPerson($id = 0, &$xml) {
return $xml->xpath("//item[id='$id']")[0]; // PHP >= 5.4 required
}
$xml = simplexml_load_string($x); // assume XML in $x
Now, you can (example 1):
echo getPerson(5, $xml)->name;
Output:
Blake
or (example 2):
$a = getPerson(2, $xml);
echo "$a->name has $a->eye eyes and $a->hair hair.";
Output:
Jake has Hazel eyes and White hair.
see it working: http://codepad.viper-7.com/SwLids
EDIT In your HTML, this would probably look like this:
...
<h1>Staff Profiles</h1>
<h4>Maintenance</h4>
<p>Maintenance staff does a lot of work! Meet your super maintenance staff:</p>
<?php
$p = getPerson(4, $xml);
echo "Name: $p->name <br />";
echo "Eye Color: $p->eye <br />";
echo "Hair Color: $p->hair <br />";
?>
no looping required, though.
First thing that popped into my mind is to use a numerical offset (which is zero-based in SimpleXML) as there is a string co-relation between the offset and the ID, the offset is always the ID minus one:
$items = $xml->item;
$id = 3;
$person = $items[$id - 1];
echo $person->id, "\n"; // prints "3"
But that would work only if - and only if - the first element would have ID 1 and then each next element the ID value one higher than it's previous sibling.
Which we could just assume by the sample XML given, however, I somewhat guess this is not the case. So the next thing that can be done is to still use the offset but this time create a map between IDs and offsets:
$items = $xml->item;
$offset = 0;
$idMap = [];
foreach ($items as $item) {
$idMap[$item->id] = $offset;
$offset++;
}
With that new $idMap map, you then can get each item based on the ID:
$id = 3;
$person = $items[$idMap[$id]];
Such a map is useful in case you know that you need that more than once, because creating the map is somewhat extra work you need to do.
So let's see if there ain't something built-in that solves the issue already. Maybe there is some code out there that shows how to find an element in simplexml with a specific attribute value?
SimpleXML: Selecting Elements Which Have A Certain Attribute Value (Reference Question)
Read and take value of XML attributes - Especially because of the answer on how to add the functionality to SimpleXMLElement transparently.
Which leads to the point you could do it as outlined in that answer that shows how it works transparently like this:
$person = $items->attribute("id", $id);
I hope this is helpful.
Related
Hi I have never used xml but need to now, so I am trying to quickly learn but struggling with the structure I think. This is just to display the weather at the top of someones website.
I want to display Melbourne weather using this xml link ftp://ftp2.bom.gov.au/anon/gen/fwo/IDV10753.xml
Basically I am trying get Melbourne forecast for 3 days (what ever just something that works) there is a forecast-period array [0] to [6]
I used this print_r to view the structure:
$url = "linkhere";
$xml = simplexml_load_file($url);
echo "<pre>";
print_r($xml);
and tried this just to get something:
$url = "linkhere";
$xml = simplexml_load_file($url);
$data = (string) $xml->forecast->area[52]->description;
echo $data;
Which gave me nothing (expected 'Melbourne'), obviously I need to learn and I am but if someone could help that would be great.
Because description is an attribute of <area>, you need to use
$data = (string) $xml->forecast->area[52]['description'];
I also wouldn't rely on Melbourne being the 52nd area node (though this is really up to the data maintainers). I'd go by its aac attribute as this appears to be unique, eg
$search = $xml->xpath('forecast/area[#aac="VIC_PT042"]');
if (count($search)) {
$melbourne = $search[0];
echo $melbourne['description'];
}
This is a working example for you:
<?php
$forecastdata = simplexml_load_file('ftp://ftp2.bom.gov.au/anon/gen/fwo/IDV10753.xml','SimpleXMLElement',LIBXML_NOCDATA);
foreach($forecastdata->forecast->area as $singleregion) {
$area = $singleregion['description'];
$weather = $singleregion->{'forecast-period'}->text;
echo $area.': '.$weather.'<hr />';
}
?>
You can edit the aforementioned example to extract the tags and attributes you want.
Always remember that a good practice to understand the structure of your XML object is printing out its content using, for instance, print_r
In the specific case of the XML you proposed, cities are specified through attributes (description). For this reason you have to read also those attributes using ['attribute name'] (see here for more information).
Notice also that the tag {'forecast-period'} is wrapped in curly brackets cause it contains a hyphen, and otherwise it wouldn generate an error.
Novice PHP programmer here (if I can even call myself that). I am currently working on a way to loop through an XML document to grab every instance of a child object that does not contain a specific keyword and then display the results. I am able to do the aforementioned but only with the first child object that is found.
Examples Follow ...
Example XML There may be more than one instance of the following and I am trying to get the <title> child element from every instance that does not contain the word "Flood" or the phrase "There are no active watches, warnings or advisories".
<entry>
<id>http://alerts.weather.gov/cap/wwaatmget.php?x=OHC095&y=0</id>
<updated>2013-04-16T20:00:01+00:00</updated>
<author>
<name>w-nws.webmaster#noaa.gov</name>
</author>
<title>There are no active watches, warnings or advisories</title>
<link href='http://alerts.weather.gov/cap/wwaatmget.php?x=OHC095&y=0'/>
</entry>
Variables
//Lucas Co. Weather
$xml_string = file_get_contents("../../cache/weather_alerts-lucas.xml");
$weather_alerts_lucas = simplexml_load_string($xml_string);
$lucas_alert = ($weather_alerts_lucas->entry->title);
$no_alert =('There are no active watches, warnings or advisories');
Displaying The Results The first if statement determines whether there should be a scroll bar or not and the second if statement determines if a specific county should display information (there are multiple counties but I am only showing one here for simplicity).
if ("$lucas_alert"=="$no_alert" || fnmatch("*Flood*", $lucas_alert))
{
//Do Nothing.
} else {
echo "<div id='emergency_bar'>";
echo "<span class='scroll_font' ... ...'>";
if ("$lucas_alert"=="$no_alert" || fnmatch("*Flood*", $lucas_alert))
{
//Do Nothing.
} else {
echo "Lucas Co. - " . $lucas_alert . " // ";
}
echo "</span>";
echo "</div>";
}
If I do it the way it is posted above it will only grab the first result and even if it grabbed multiple instances of <title> it would not display anything if one of the instances had the word "Flood" in it.
Thanks in advance. I'm not looking for someone to write code for me, just some direction.
$xml = simplexml_load_string($x); // XML in $x
$no = "There are no active watches, warnings or advisories";
foreach ($xml->xpath("//title") as $title) {
if ($title <> $no && strpos($title,"Flood") === false) echo "$title<br />";
}
see it working: http://codepad.viper-7.com/JVOoke
I am trying to search for a php variable in an xml file using xpath, but failing miserably. It works with a hard coded value, so I am nearly there..
<visitors>
<detail>
<id>876867</id>
<name>Bob McHaggis</name>
<email>bob#gmail.com</email>
</detail>
<detail>
<id>897987</id>
<name>Mark McBob</name>
<email>mark#gmail.com</email>
</detail>
</visitors>
<?php $sxe = simplexml_load_file("/CaptivePortal/visitors.xml");
foreach($sxe->xpath('//visitors/detail') as $item){
$row = simplexml_load_string($item->asXML());
$v = $row->xpath('//id[. ="'.$_COOKIE["judsons"].'"]');
} echo $v[0]; ?>
This works great checks for an id against the id stored in the cookie. But based on that value being found how do I access the name and email for the key matched?
Found & matched: 897987
I want to echo the name and email to, so based on that is Mark McBob & mark#gmail.com
My initial advice would be to take a good few minutes to (re-)read through the SimpleXML Basic Usage page in the PHP manual.
For selecting a single item within the XML file, there is no need at all to loop over anything. The example code below, and why it is different from yours, should become clear after familiarising yourself with the page mentioned above.
<?php
$search_id = (int) $_COOKIE["judsons"];
$visitors = simplexml_load_file("/CaptivePortal/visitors.xml");
$details = $visitors->xpath("detail[id='$search_id']");
if (empty($details)) {
echo "No details found for '$search_id'.";
} else {
$detail = $details[0];
$name = (string) $detail->name;
$email = (string) $detail->email;
echo "$name's email is $email";
}
The idea above is that $details will be an array containing hopefully just one <detail> element. It could be an empty array if no <detail> was found with the specified <id>, which is what the if(empty(…)) checks for. If the $details array is not empty, we're really only interested in the first one ($details[0]).
To access the information available within the <detail>, as explained on the "basic usage" page, the elements can be accessed with normal object property syntax ($detail->name). Doing so returns an object for that item (e.g. the <name>), so to get at the value as a string the object is type cast using (string).
You're actually doing quite a bit you don't need to there. For one thing, you don't need the loop. For another, inside your loop, you convert the context XML into a string (::asXML()), then convert it back into XML (simplexml_load_string()).
All you need is:
$xml = "<visitors><detail><id>876867</id><name>Bob McHaggis</name><email>bob#gmail.com</email></detail><detail><id>897987</id><name>Mark McBob</name><email>mark#gmail.com</email></detail></visitors>";
$sxe = simplexml_load_string($xml);
$row = $sxe->xpath('detail[id = '.$_COOKIE["judsons"].']');
That gets you the row. To extract part of it:
$name = $row[0]->xpath('name');
echo $name[0]; //Mark McBob
Update:
Thanks Rambo for the great answer. The only issue that I have now is that it only displays artist information so long as the artists next gig is in the UK. For example, if they're playing in France and THEN the UK - it won't display anything (Or it will display my else message). If their next gig IS in the UK, then it will echo artist information etc. Any idea how to get it to echo only UK information, regardless if they're in another country before hand?
Thank you.
Original Post:
I'm currently creating a website for my final major project. I retrieve data using the Last.fm API using PHP and XML. It's going well so far, but there are a few issues I'm having trouble with. I'm very new to PHP, so I want to use this opportunity to develop some skills.
I want to limit the data to my city or country.
How do I retrieve images from an XML document?
Using the last.fm API, more specifically, the artist.getEvents (http://bit.ly/zYzWo6) - I am able to create a basic search field so that the user can type in an artist name. This is a great step in the right direction, but the problem is, any results outside of my country is irrelevant for my project. Using artist.getEvents doesn't allow any specific parameters such as location - geo.getEvents (http://bit.ly/wpSQwd) does however.
The following is the code used for my basic search:
<?php
$first_bit_of_url = 'http://ws.audioscrobbler.com/2.0/?method=artist.getevents&artist=';
$last_bit_of_url = '&api_key=b25b959554ed76058ac220b7b2e0a026&d';
$artist = ($_GET["artist"]); // gets the information passed by the input form
$query_url = $first_bit_of_url . $artist . $last_bit_of_url ;
$upcoming_gig_data_xml = simplexml_load_file($query_url);
$search_result = $upcoming_gig_data_xml->events->event->artists->artist;
$venue_result = $upcoming_gig_data_xml->events->event->venue->name;
$city_result = $upcoming_gig_data_xml->events->event->venue->location->city;
for ($i = 0; $i < 5; $i++){
echo $search_result . "<br />";
echo $venue_result . ", ";
echo $city_result . "<br />";
} ?>
Secondly how would I go about retrieving an image from, for example, this sample of XML code used in the above context? I've briefly read some articles on Xpath, can I mix the Xpath method with the method I'm using above?
<image size="small">...</image>
<image size="medium">...</image>
<image size="large">...</image>
Hopefully someone can point me in the right direction here, and I appreciate any help given.
Thanks,
Chris.
You could use XPath to get only the event elements with UK venues.
$lfm = simplexml_load_file('http://ws.audioscrobbler.com/2.0/?method=artist.getevents&artist=metallica&api_key=b25b959554ed76058ac220b7b2e0a026&d');
$uk_events = $lfm->xpath('events/event[venue/location/country="United Kingdom"]');
foreach ($uk_events as $event) {
$venue_city = (string) $event->venue->location->city;
$large_pics = array_map('strval', $event->xpath('image[#size="large"]'));
// Do whatever other processing/displaying you like…
}
(See it running.)
for your second question: use simple xml http://www.php.net/manual/de/ref.simplexml.php
for example your xml might be:
<?xml version="1.0" encoding="UTF-8"?>
<images>
<image size="small">1</image>
<image size="medium">2</image>
<image size="large">3</image>
</images>
load the xml file with simple xml and access the nodes like this. This is just a simple example.
$r = simplexml_load_file('test.xml');
foreach($r->image as $img) {
print $img . ' and size is ' . $img['size'] . "<br/>";
}
$smallimg = $upcoming_gig_data_xml->events->event->venue->image['small'];
then you can echo the image out using the img html tag, giving it's src the value of $smallimg
I haven't tested this, but hopefully it'll get you in the right direction
update
for the first point, loop through the xml file and do a match for the country so if it is equal to the united kingdom then process everything, otherwise it will skip it
foreach($upcoming_gig_data_xml->events->event as $event)
{
if($event->location == "United Kingdom")
{
// process everything here
}
}
here is the page i want to parse
(the api link i gave is just a dev test so its ok to be public)
http://api.scribd.com/api?method=docs.getList&api_key=2apz5npsqin3cjlbj0s6m
the output im looking for is something like this (for now)
Doc_id: 29638658
access_key: key-11fg37gwmer54ssq56l3
secret_password: 1trinfqri6cnv3gf6rnl
title: Sample
description: k
thumbnail_url: http://i6.scribdassets.com/public/images/uploaded/152418747/xTkjCwQaGf_thumbnail.jpeg
page_count: 100
ive tried everything i can find on the internet but nothing works good. i have this one script
<?php
$xmlDoc = new DOMDocument();
$xmlDoc->load("http://api.scribd.com/api?method=docs.getList&api_key=2apz5npsqin3cjlbj0s6m");
$x = $xmlDoc->documentElement;
foreach ($x->childNodes AS $item) {
print $item->nodeName . " = " . $item->nodeValue;
}
?>
its output comes out like this:
#text =
resultset =
29638658
key-11fg37gwmer54ssq56l3
1trinfqri6cnv3gf6rnl
Sample
k
http://i6.scribdassets.com/public/images/uploaded/152418747/xTkjCwQaGf_thumbnail.jpeg
DONE
100
29713260
key-18a9xret4jf02129vlw8
25fjsmmvl62l4cbwd1vq
book2
description bla bla
http://i6.scribdassets.com/public/images/uploaded/153065528/oLVqPZMu3zhsOn_thumbnail.jpeg
DONE
7
#text =
i need major help im really stuck and dont know what to do. please please help me. thnx
I recommend you load the XML data into a new SimpleXmlElement object as this will allow you to run xpath queries against the document.
You will need to do a little research on how it works, but here's a few pointers...
Execute the xpath like so:
// $xml is a SimpleXMLElement object
$xml = simplexml_load_file('/path/to/file');
$nodes = $xml->xpath('/xpathquery');
A single / represents the root node (in your case rsp). A double slash represents any matching node. For example //title would return all titles. Each result of an xpath query is an array of SimpleXMLElements. You can get data from it like so:
# Untested
$xml = simplexml_load_file('/path/to/file');
$nodes = $xml->xpath('//result');
foreach ($result as $node) {
// Print out the value in title
echo $node->title;
}
// Print out the amount of results
echo $xml->rsp->attributes()->totalResultsAvailable;
The final example with the results numbers may not work, but it is along those lines.
With "$x->childNodes" you get only direct childs. You might want to check php documentation:
http://php.net/manual/en/class.domdocument.php
And I think that this one could be better:
http://si.php.net/manual/en/book.simplexml.php
Quick and dirty, the way things are supposed to be:
<?php
$x = simplexml_load_file('http://api.scribd.com/api?method=docs.getList&api_key=2apz5npsqin3cjlbj0s6m');
foreach($x->resultset->result as $v) {
foreach((array)$v as $kk=>$vv) {
echo $kk.": ".trim($vv)."<br>\n";
}
echo "<br><br>\n";
}
This will output in the format you requested:
doc_id: 29638658
access_key: key-11fg37gwmer54ssq56l3
secret_password: 1trinfqri6cnv3gf6rnl
title: Sample
description: k
thumbnail_url: http://i6.scribdassets.com/public/images/uploaded/152418747/xTkjCwQaGf_thumbnail.jpeg
conversion_status: DONE
page_count: 100
doc_id: 29713260
access_key: key-18a9xret4jf02129vlw8
secret_password: 25fjsmmvl62l4cbwd1vq
title: book2
description: description bla bla
thumbnail_url: http://i6.scribdassets.com/public/images/uploaded/153065528/oLVqPZMu3zhsOn_thumbnail.jpeg
conversion_status: DONE
page_count: 7