0

I have an XML doc similar to:

<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:zapi="http://zotero.org/ns/api">
  <link rel="up" type="application/atom+xml" href="some link"/>
</entry>

I want the "up" (rel attribute) of the link element, so I do this:

$xml = new SimpleXMLElement($body);
$php_up = $xml->xpath("//link[@rel='up']");
var_dump($php_up); exit;

which just gives me array(0) { }.

What am I doing wrong?

Leo Bedrosian
  • 3,659
  • 2
  • 18
  • 22
Leo
  • 4,136
  • 6
  • 48
  • 72
  • Thanks for the edit, @Haig Bedrosian! – Leo Oct 21 '14 at 22:37
  • Please use the search before asking a question. XML SimpleXML XPath and empty array should have guided you to that (duplicate) - but sure there are also [other](http://stackoverflow.com/q/20124874/367456) very similar Q&A material already available. Good luck! – hakre Oct 22 '14 at 13:08
  • Thanks @hakre, I do not know why I missed that one. :-( – Leo Oct 23 '14 at 01:30
  • I just tested a new question and realize that I depended on that the relevant questions show up when editing. This did not work right now however. – Leo Oct 23 '14 at 01:39
  • 1
    Yes, it's also okay to just first search. I normally use an internet search engine. I then also put in to search a specific site, like stackoverflow. – hakre Oct 23 '14 at 12:41
  • I do that too, but sometimes you just fail. :-( – Leo Oct 23 '14 at 13:27
  • 1
    No problem. That's why I normally help searching as well. There are some common questions already covered, so it's often good to mark the duplicates to keep the site in a better shape. – hakre Oct 25 '14 at 09:10

3 Answers3

1

You don't need XPATH to get the REL attribute. This should be as simple as:

$php_up = $xml->link->attributes()->rel;

Updated: To get the attributes of multiple link elements:

The XML:

<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:zapi="http://zotero.org/ns/api">
  <link rel="up" type="application/atom+xml" href="some link"/>
  <link rel="down" type="application/atom+xml" href="some link"/>
</entry>

The PHP:

$xml = new SimpleXMLElement($body);

foreach ($xml->link as $link) {
    $rels[] = (string)$link->attributes()->rel;
}
var_dump($rels); exit;
Leo Bedrosian
  • 3,659
  • 2
  • 18
  • 22
  • There are in fact several `link` elements so I guess I need XPATH? – Leo Oct 21 '14 at 16:43
  • 1
    You can iterate over the link elements using `foreach ()` to then get the attributes of each individual link. Depending on what you're trying to do, it might the best option. – Leo Bedrosian Oct 21 '14 at 17:07
1

The declaration in the second line of your XML gives two XML namespaces:

<entry xmlns="http://www.w3.org/2005/Atom" xmlns:zapi="http://zotero.org/ns/api">

The first xmlns attribute, with no colon-separated suffix, sets the "default" namespace for the entry element as being http://www.w3.org/2005/Atom, and descendant nodes with no prefix are in that namespace by default. To be able to access these elements with a "default" namespace using XPath, you need to set a namespace using registerXPathNamespace, and then perform your query, prefixing the element you're looking for (link) with the namespace:

$xml->registerXPathNamespace('d', 'http://www.w3.org/2005/Atom');
$php_up = $xml->xpath("//d:link[@rel='up']");
var_dump($php_up);

Output:

array(1) {
  [0] =>
  class SimpleXMLElement#2 (1) {
    public $@attributes =>
    array(3) {
      'rel' =>
      string(2) "up"
      'type' =>
      string(20) "application/atom+xml"
      'href' =>
      string(9) "some link"
    }
  }
}
i alarmed alien
  • 9,412
  • 3
  • 27
  • 40
0

It's not going to be obvious, but you need to use str_replace on your xmlns :)

$body= str_replace('xmlns=', 'ns=', $body);

Before you call your $xml = new SimpleXMLElement($body);

Here's your eval.in

Ohgodwhy
  • 49,779
  • 11
  • 80
  • 110
  • 1
    Thanks! It works. ;-) -- But why do I have to do that? There is no namespace involved in this tag or any parent tag. Except for the namespace declarations, of course. – Leo Oct 21 '14 at 16:46
  • 1
    @leo I genuinely don't know. I had this problem originally years ago, and I've just remembered to do it ever since when the ns is xmlns. If someone does know why, I'd love to hear. My assumption is it's due to the parsing engine, I just haven't taken the time to look around. – Ohgodwhy Oct 21 '14 at 17:07
  • Ok, thanks. And thanks for eval.in which I have not seen before! – Leo Oct 21 '14 at 17:13
  • 3
    @Ohgodwhy `xmlns=...` sets a default namespace--all elements declared beneath it are in that namespace unless they have another namespace specified (i.e. ``). The `str_replace` hack just removes the default namespace. XPath doesn't have the concept of default namespaces, so (unfortunately) you have to register a NS and specify it in the query. – i alarmed alien Oct 21 '14 at 18:18
  • 1
    @Ohgodwhy: You should stop using `str_replace` on XML. And please restrain from suggesting that as answers. It's one thing to not know the answer but another one to prevent others from learning what XML namespaces are. And if you don't know something, just wrap it into a question and ask here on SO. – hakre Oct 22 '14 at 13:05