5

youtube.xml

<feed xmlns="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:gd="http://schemas.google.com/g/2005" xmlns:yt="http://gdata.youtube.com/schemas/2007">  

    <entry>
        ...
        <yt:duration seconds="1870"/>
        ...
    </entry>

</feed>

update_videos.php

$source = 'youtube.xml';

// load as file
$youtube = new SimpleXMLElement($source, null, true);

foreach($youtube->entry as $item){
    //title works
    echo $item->title;

    //now how to get seconds? My attempt...
    $namespaces = $item->getNameSpaces(true);
    $yt = $item->children($namespaces['yt']);
    $seconds = $yt->duration->attributes();
    echo $seconds['seconds'];
    //but doesn't work :(
}   
hakre
  • 193,403
  • 52
  • 435
  • 836
Jonathan
  • 3,016
  • 9
  • 43
  • 74
  • possible duplicate of [Parse XML with Namespace using SimpleXML](http://stackoverflow.com/questions/595946/parse-xml-with-namespace-using-simplexml) – hakre Jul 05 '11 at 01:24
  • @hakre, not sure if it's a duplicate. I don't see where the questioner is asking how to get `attribute` of a node with a namespace. – Jonathan Jul 05 '11 at 01:31
  • The duplicate is only possible. I think your question is valid, I wonder why there is the info that hard to find. http://stackoverflow.com/questions/3438900/simplexml-add-a-new-node-using-a-namespace-previously-declared-how and check this: http://blog.sherifmansour.com/?p=302 - was just googling for simplexml and namespaces – hakre Jul 05 '11 at 08:05
  • @hakre, thanks for the links. I actually came across that before I posted the question. There does seem to be a lot of info on namespaces and attributes individually, which seem pretty easy to understand, but nothing on how to parse when they are combined. Which is why I posted the question. :) – Jonathan Jul 05 '11 at 21:50
  • What do you mean by "combined"? One element can only be part of one namspace AFAIK there can not be a single element being part of more than one namespace in a document. – hakre Jul 05 '11 at 21:53
  • I'll rephrase. All the examples I found showed me how to get `` or `text` separately, which I know how to do, but I have found no information on how to get the attribute when it is in a tag with namespace ``. Which is why I posted the question looking for guidance on where im going wrong. – Jonathan Jul 06 '11 at 01:48
  • 1
    The code in the question now appears to work as expected, at least in PHP 5.2 and up in [this testbed](http://codepad.viper-7.com/HPJyD7). The problem may have been that that the `seconds` attribute is not considered to be in the `yt` namespace, even though the `duration` element is, [as discussed elsewhere](http://stackoverflow.com/questions/10672635/xml-namespaces-and-unprefixed-attributes). This requires you to select the `NULL` namespace to get the attributes, which perhaps didn't work in some older version of PHP? – IMSoP Jun 06 '13 at 17:57
  • 1
    @IMSoP: [I also can confirm your findings](http://stackoverflow.com/a/17545843/367456). My conclusion is the follwoing: The OP didn't provide any decent error description, so it's hard to say what exactly the problem was. One can only assume so I suggest to put this question on hold until more information is provided. If possible after that much time fine. If not, this question needs to be degraded as it's not specific about what it asks and stands in the way for the common problem it is about. – hakre Jul 09 '13 at 10:28
  • @hakre I asked this question in 2011, back when I was just started programming but I can see how it is a bad question now so excuse me for that. – Jonathan May 14 '14 at 16:37

4 Answers4

10

The code you've got in your question does work. You have correctly written that you can access an attribute from a namespaced-element that is not in the default namespace as the root-element by making use of the SimpleXMLElement::children() method with the XML-namespace related parameters $ns and $is_prefix:

$youtube->entry->children('yt', TRUE)->duration->attributes()->seconds; // is "1870"

As this is basically the same as you did in your question, one could say that you have answered your own question. Compare with the extended Online Demo #2.


Long answer: The code you've got in your question does work. You can find your example XML and code in an interactive example online here: Online Demo #1 - it shows the result with different PHP and LIBXML versions.

Code:

$buffer = '<feed xmlns="http://www.w3.org/2005/Atom" xmlns:yt="http://gdata.youtube.com/schemas/2007">
    <entry>
        <yt:duration seconds="1870"/>
    </entry>
</feed>';

$xml = new SimpleXMLElement($buffer);

echo "ibxml version: ", LIBXML_DOTTED_VERSION, "\n";

foreach ($xml->entry as $item)
{
    //original comment: how to get seconds?
    $namespaces = $item->getNameSpaces(true);
    $yt         = $item->children($namespaces['yt']);
    $seconds    = $yt->duration->attributes();

    echo $seconds['seconds'], "\n"; // original comment: but doesn't work.
}

echo "done. should read 1870 one time.\n";

Results:

Output for 5.3.26, 5.4.16 - 5.5.0

ibxml version: 2.9.1
1870
done. should read 1870 one time.

Output for 5.3.15 - 5.3.24, 5.4.5 - 5.4.15

ibxml version: 2.8.0
1870
done. should read 1870 one time.

Output for 5.1.2 - 5.3.14, 5.4.0 - 5.4.4

ibxml version: 2.7.8
1870
done. should read 1870 one time.

From this perspective everything looks fine. As you have not given any concrete error description it's hard to say what went wrong on your case. You were probably using a PHP version that was outdated the time you asked your question, for example getting a fatal error:

Fatal error: Call to undefined method SimpleXMLElement::getNameSpaces()

Probably also due to an outdated libxml version. According to the test, the following libxml versions work fine with PHP 5.1.2-5.5.0:

  • ibxml version: 2.9.1
  • ibxml version: 2.8.0
  • ibxml version: 2.7.8
hakre
  • 193,403
  • 52
  • 435
  • 836
3

So I found a way to do it using xpath, is this the best way or is there a way that's consistent with my code in the question? Just out of curiosity.

$source = 'youtube.xml';

// load as file
$youtube = new SimpleXMLElement($source, null, true);
$youtube->registerXPathNamespace('yt', 'http://gdata.youtube.com/schemas/2007');

$count = 0;
foreach($youtube->entry as $item){

    //title works
    echo $item->title;

    $attributes = $item->xpath('//yt:duration/@seconds');
    echo $attributes[$count]['seconds'];
    $count++;
}
Jonathan
  • 3,016
  • 9
  • 43
  • 74
2

The following is another solution using attributes() and children() only, tested and working OK! :)

$data=<<<XML

<rss version="2.0" xmlns:media="http://search.yahoo.com/mrss/">
 <channel>
   <item>
    <title>Title: 0</title>
    <link test="test" test2="This is a test">Link:0</link>
    <media:thumbnail url="Thumbnail URL: 0"/>
    <media:content url="Content URL: 0" type="video/mp4" />
  </item>
    <item>
    <title>Title: 1</title>
    <link>Link:1</link>
    <media:thumbnail url="1"/>
    <media:content url="1" type="video/mp4" />
  </item>
 </channel>
</rss>

XML;



$xml=simplexml_load_string($data);

//reading simple node
echo $xml->channel[0]->item[0]->title;

echo "<br>----------------------------------<br>";

//reading/updating simple node's attribute
echo $xml->channel[0]->item[0]->link->attributes()->test2="This is a NEW test!.";

echo "<br>----------------------------------<br>";

//reading/updating namespaced node's attribute
echo $xml->channel[0]->item[0]->children('media',TRUE)->content->attributes()->type="VIDEO/MP6";

//saving...
$xml->asXml('updated.xml');
angelcool.net
  • 2,505
  • 1
  • 24
  • 26
2

I was struggling with this situation as well then I found the solution. It's so simple on how to read a complex XML attribute namespace.

foreach($media->group->content as $content) {
    $attrs = $content->attributes();
    $ytformat = $content->attributes("yt","format");

    echo(" attr = " . $ytformat . "; ");

    if ($ytformat == 5) {
        $url = $attrs['url'];
        $type = $attrs['type'];

        echo ($url . " - " . $type);
    }
}

Hope it helps...:)

dviana
  • 29
  • 4
  • 1
    Your call to `attributes()` is working only accidentally - the second argument is a boolean `$is_prefix`, and the string `'format'` is being interpreted as `true`. I think what you were after is `$content->attributes("yt", true)->format` – IMSoP Jun 06 '13 at 17:39