I wrote code that get data from http://ax.itunes.apple.com/WebObjects/MZStoreServices.woa/ws/RSS/topsongs/limit=10/xml
for($i = 0; $i < 10; $i++){
$title = $xml->entry[$i]->title; // work
$name = $xml->entry[$i]->im:name; //does not work
$html .= "<br />$title<hr />";} echo $html;
The problem is im .I cannot get data of it . How can I solve it ?
You can not access <im:name> in that way.
im: is a NameSpace prefix: to work with namespaced tags you have to use a specific syntax.
You can retrieve all document’s Namespaces URI using:
$namespaces = $xml->getDocNamespaces();
and you will obtain this array:
Array
(
[im] => http://itunes.apple.com/rss
[] => http://www.w3.org/2005/Atom
)
Each array key is the Namespace prefix, each array value is the Namespace URI. The URI with an empty key represents global document Namespace URI.
SimpleXML has not the best syntax to work with Namespaces (IMO DOMDocument is a better choice). In your case, you have to do in this way:
$children = $xml->entry[$i]->children( 'im', True );
echo $children->artist . '<br>';
echo $children->name . '<br>';
Result for ->entry[0]:
Prince & The Revolution
Purple Rain
The ->children second parameter True means “regard first parameter as prefix”; as alternative, you can use complete Namespace URI in this way:
$children = $xml->entry[$i]->children( 'http://itunes.apple.com/rss' );
... ♪ I only wanted to see you laughing in the purple rain ♪ ...
Related
I am trying to utilize simplexml to convert an iTunes RSS Feed to JSON so I can better parse it. The issue I am having is that it is not coming back as correctly formatted JSON.
$feed_url = 'https://podcasts.subsplash.com/c2yjpyh/podcast.rss';
$feed_contents = file_get_contents($feed_url);
$xml = simplexml_load_string($feed_contents);
$podcasts = json_decode(json_encode($xml));
print_r($podcasts);
Is there a better way to be attempting this to get the correct result?
Thanks to IMSoP for pointing me in the right direction! This took a bit of studying but the solution ends up being very simple! Instead of trying to convert to a JSON format, just use SimpleXML. However, due to the namespaces, it does require an additional line to map the itunes: prefix.
So in my iTunes feed rss, the following line exists: xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd So we just reference this to make accessing the values very easy. Here is a quick example:
$rss = simplexml_load_file('https://podcasts.example.com/podcast.rss');
foreach ($rss->channel->item as $item){
// Now we define the map for the itunes: namespace
$itunes = $item->children('http://www.itunes.com/dtds/podcast-1.0.dtd');
// This is a value WITHOUT the itunes: namespace
$title = $item->title;
// This is a value WITH the itunes: namespace
$author = $itunes->author;
echo $title . '<br>';
echo $author . '<br>';
}
The other little issue that I ran into is getting attributes such as the url for images and audio links. That is accomplished by using the attributes() function like so:
// Access attributes WITH itunes: namespace
$image = $itunes->image->attributes();
// Access attributes WITHOUT itunes: namespace
$audio = $item->enclosure->attributes();
// To echo these we simple add the desired attribute in `[]`:
echo $image['href'] . '<br>';
echo $audio['url'] . '<br>';
I'm not so sure about the title, will try to explain in the next lines.
I have an xml file like this :
<CAR park="3" id="1" bay="0">
<SITE_ID>0</SITE_ID>
<SITE_NAME>Car Seller 1</SITE_NAME>
. . .
</CAR>
I am sucessfully iterating through my xml to get all the data.
But, I want to be able to filter by bays. I want to do something like
$xml = simplexml_load_file('myfile.xml');
$x = 1;
foreach($xml as $car) {
if($car->bay == '0'){
echo $car->SITE_ID;
$x++;
}
}
You can use XPath to fetch only the bay 0 cars...
$bay0 = $xml->xpath('//CAR[#bay="0"]');
foreach ( $bay0 as $car ) {
echo $car->SITE_ID.PHP_EOL;
}
The XPath statement is simply - any CAR element that has an attribute bay with the value 0 in it.
In case you need to access attributes in other cases, with SimpleXML - you access them as though they are array elements, so it would be $car['bay'] in the code you had above.
I am using PHP and simpleXML to read the following rss feed:
http://feeds.bbci.co.uk/news/england/rss.xml
I can get most of the information I want like so:
$rss = simplexml_load_file('http://feeds.bbci.co.uk/news/england/rss.xml');
echo '<h1>'. $rss->channel->title . '</h1>';
foreach ($rss->channel->item as $item) {
echo '<h2>' . $item->title . "</h2>";
echo "<p>" . $item->pubDate . "</p>";
echo "<p>" . $item->description . "</p>";
}
But how would I output the thumbnail image that is in the following tag:
<media:thumbnail width="66" height="49" url="http://news.bbcimg.co.uk/media/images/51078000/jpg/_51078953_226alanpotbury.jpg"/>
As you already know, SimpleXML lets you select an node's child using the object property operator -> or a node's attribute using the array access ['name']. It's great, but the operation only works if what you select belongs to the same namespace.
If you want to "hop" from a namespace to another, you can use the children() or attributes() methods. In your case, this is made a bit trickier because you have <item/> in the global namespace, the node you're looking for is in the "media" namespace* and then the attributes are in the global namespace again (they are not prefixed.) So using the normal object/array notation you'll have to "hop" twice:
foreach ($rss->channel->item as $item)
{
// we load the attributes into $thumbAttr
// you can either use the namespace prefix
$thumbAttr = $item->children('media', true)->thumbnail->attributes();
// or preferably the namespace name, read note below for an explanation
$thumbAttr = $item->children('http://search.yahoo.com/mrss/')->thumbnail->attributes();
echo $thumbAttr['url'];
}
*Note
I refer to the namespace as the "media" namespace but that's not really correct. The namespace name is http://search.yahoo.com/mrss/, and "media" is just a prefix, some sort of alias if you will. What's important to keep in mind is that http://search.yahoo.com/mrss/ is the real name of the namespace. At some point, your RSS provider might decide to change the prefix to, say, "yahoo" and your script will stop working if your script refers to the "media" prefix. However, if you use the namespace name, it will keep working no matter the prefix.
SimpleXML is pretty bad at handling namespaces. You have two choices: The simplest hack is to simply read the contents of the feed into a string and replace the namespaces;
$feed = file_get_contents('http://feeds.bbci.co.uk/news/england/rss.xml');
$feed = str_replace('<media:', '<', $feed);
$rss = simplexml_load_string($feed);
...
Now you can access the element thumbnail directly.
The more elegant (not really) method is to find out what URI the namespace uses. If you look at the source code for http://feeds.bbci.co.uk/news/england/rss.xml you see that it points to http://search.yahoo.com/mrss/.
Now you can use this URI in the children() method of a SimpleXMLElement to get the contents of the media:thumbnail element;
$rss = simplexml_load_file('http://feeds.bbci.co.uk/news/england/rss.xml');
foreach ($rss->channel->item as $item) {
$media = $item->children('http://search.yahoo.com/mrss/');
...
}
I've read through a bunch of posts here but I still can't figure out how to sort the data I am reading from an OPML file using simplexml functions. I realize this is kind of a duplicate, but I'm apparently too slow to get this right using abstracted examples.
I have a pretty standard OPML file with contents like:
<?xml version="1.0" encoding="UTF-8"?>
<opml version="1.0">
<!-- OPML generated by Fever -->
<head><title>Fever // Coffee</title></head>
<body>
<outline type="rss" text="I Love Coffee" title="I Love Coffee" xmlUrl="http://en.ilovecoffee.jp/posts/rss" htmlUrl="http://www.ilovecoffee.jp"/>
<outline type="rss" text="Dear Coffee I Love You" title="Dear Coffee I Love You" xmlUrl="http://feeds.feedburner.com/DearCoffeeILoveYou" htmlUrl="http://www.dearcoffeeiloveyou.com"/>
</body>
</opml>
I am generating a Markdown list using the simplest possible code:
foreach ($opml->body->outline as $feed) {
echo '* [' . $feed['title'] . '](' . $feed[htmlUrl] . ')' . "\n";
}
I simply want to sort the list by the "title" attribute, but I can't get my head around how to do so.
It's my understanding I need to convert the xml object into an array, which I can do with:
$json = json_encode($opml);
$xml_array = json_decode($json,TRUE);
But I can't seem to get things right to sort that array by the "title"
Rather than trying to blindly convert the whole XML document into an array (which will rarely be a particularly useful array), you should build an array with the items you want:
$feed_list = array();
foreach ($opml->body->outline as $feed) {
$feed_list[] = $feed;
}
You can then use usort() to sort the items by whatever condition you want, e.g. using strcasecmp() to give a case-insensitive alphanumeric order:
usort($feed_list, function($a, $b) {
// $a and $b are SimpleXMLElement objects which are being sorted
return strcasecmp( (string)$a['title'], (string)$b['title'] );
});
You now have a sorted array to pass to your existing display logic:
foreach ( $feed_list as $feed ) {
echo '* [' . $feed['title'] . '](' . $feed['htmlUrl'] . ')' . "\n";
}
Below is the XML I am working with - there are more items - this is the first set. How can I get these elements in to an array? I have been trying with PHP's SimpleXML etc. but I just cant do it.
<response xmlns:lf="http://api.lemonfree.com/ns/1.0">
<lf:request_type>listing</lf:request_type>
<lf:response_code>0</lf:response_code>
<lf:result type="listing" count="10">
<lf:item id="56832429">
<lf:attr name="title">Used 2005 Ford Mustang V6 Deluxe</lf:attr>
<lf:attr name="year">2005</lf:attr>
<lf:attr name="make">FORD</lf:attr>
<lf:attr name="model">MUSTANG</lf:attr>
<lf:attr name="vin">1ZVFT80N555169501</lf:attr>
<lf:attr name="price">12987</lf:attr>
<lf:attr name="mileage">42242</lf:attr>
<lf:attr name="auction">no</lf:attr>
<lf:attr name="city">Grand Rapids</lf:attr>
<lf:attr name="state">Michigan</lf:attr>
<lf:attr name="image">http://www.lemonfree.com/images/stock_images/thumbnails/2005_38_557_80.jpg</lf:attr>
<lf:attr name="link">http://www.lemonfree.com/56832429.html</lf:attr>
</lf:item>
<!-- more items -->
</lf:result>
</response>
Thanks guys
EDIT: I want the first items data in easy to access variables, I've been struggling for a couple of days to get SimpleXML to work as I am new to PHP, so I thought manipulating an array is easier to do.
Why do you want them in an array? They are structured already, use them as XML directly.
There is SimpleXML and DOMDocument, now it depends on what you want to do with the data (you failed to mention that) which one serves you better. Expand your question to get code samples.
EDIT: Here is an example of how you could handle your document with SimpleXML:
$url = "http://api.lemonfree.com/listings?key=xxxx&make=ford&model=mustang";
$ns_lf = "http://api.lemonfree.com/ns/1.0";
$response = simplexml_load_file($url);
// children() fetches all nodes of a given namespace
$result = $response->children($ns_lf)->result;
// dump the entire <lf:result> to see what it looks like
print_r($result);
// once the namespace was handled, you can go on normally (-> syntax)
foreach ($result->item as $item) {
$title = $item->xpath("lf:attr[#name='title']");
$state = $item->xpath("lf:attr[#name='state']");
// xpath() always returns an array of matches, hence the [0]
echo( $title[0].", ".$state[0] );
}
Perhaps you should look at SimplePie, it parses XML feeds into an array or an object (well, one of the two :D). I think it works well for namespaces and attributes too.
Some benefits include it's GPL license (it's free) and it's support community.
SimpleXML is the best way to read/write XML files. Usually it's as easy as using arrays, except in your case because there's XML namespaces involved and it complicates stuff. Also, the format used to stored attributes kind of sucks, so instead of being easy to use and obvious it's kind of complicated, so here's what you're looking for so that you can move on to doing something more interesting for you:
$response = simplexml_load_file($url);
$items = array();
foreach ($response->xpath('//lf:item') as $item)
{
$id = (string) $item['id'];
foreach ($item->xpath('lf:attr') as $attr)
{
$name = (string) $attr['name'];
$items[$id][$name] = (string) $attr;
}
}
You'll have everything you need in the $items array, use print_r() to see what's inside. $url should be the URL of that lemonfree API thing. The code assumes there can't be multiple values for one attribute (e.g. multiple images.)
Good luck.
This is the way I parsed XML returned from a clients email address book system into an array so I could use it on a page. uses an XML parser that is part of PHP, I think.
here's it's documentation http://www.php.net/manual/en/ref.xml.php
$user_info = YOUR_XML
SETS $xml_array AS ARRAY
$xml_array = array();
// SETS UP XML PARSER AND PARSES $user_info INTO AN ARRAY
$xml_parser = xml_parser_create();
xml_parse_into_struct($xml_parser, $user_info, $values, $index);
xml_parser_free($xml_parser);
foreach($values as $key => $value)
{
// $value['level'] relates to the nesting level of a tag, x will need to be a number
if($value['level']==x)
{
$tag_name = $value['tag'];
// INSERTS DETAILS INTO ARRAY $contact_array SETTING KEY = $tag_name VALUE = value for that tag
$xml_array[strtolower($tag_name)] = $value['value'];
}
}
If you var_dump($values) you should see what level the data you're info is on, I think it'll be 4 for the above XML, so you can then filter out anything you don't want by changing the value of $value['level']==x to the required level, ie. $value['level']==4.
This should return $xml_array as an array with $xml_array['title'] = 'Used 2005 Ford Mustang V6 Deluxe' etc.
Hope that helps some