Weird SimpleXML issue - can't reference nodes

2019-03-02 02:08发布

I'm trying to parse a remote XML file, which is valid:

$xml = simplexml_load_file('http://feeds.feedburner.com/HammersInTheHeart?format=xml');

The root element is feed, and I'm trying to grab it via:

$nodes = $xml->xpath('/feed'); //also tried 'feed', without slash

Except it doesn't find any nodes.

print_r($nodes); //empty array

Or any nodes of any kind, so long as I search for them by tag name, in fact:

$nodes = $xml->xpath('//entry');
print_r($nodes); //empty array

It does find nodes, however, if I use wildcards, e.g.

$nodes = $xml->xpath('/*/*[4]');
print_r($nodes); //node found

What's going on?

1条回答
一纸荒年 Trace。
2楼-- · 2019-03-02 02:57

Unlike DOM, SimpleXML has no concept of a document object, only elements. So if you load an XML you always get the document element.

$feed = simplexml_load_file($xmlFile);
var_dump($feed->getName());

Output:

string(4) "feed"

That means that all Xpath expression have to to be relative to this element or absolute. Simple feed will not work because the context already is the feed element.

But here is another reason. The URL is an Atom feed. So the XML elements in the namespace http://www.w3.org/2005/Atom. SimpleXMLs magic syntax recognizes a default namespace for some calls - but Xpath does not. Here is not default namespace in Xpath. You will have to register them with a prefix and use that prefix in your Xpath expressions.

$feed = simplexml_load_file($xmlFile);
$feed->registerXpathNamespace('a', 'http://www.w3.org/2005/Atom');
foreach ($feed->xpath('/a:feed/a:entry[position() < 3]') as $entry) {
  var_dump((string)$entry->title);
}

Output:

string(24) "Sharing the goals around"
string(34) "Kouyate inspires Hammers' comeback"

However in SimpleXML the registration has to be done for each object you call the xpath() method on.

Using Xpath with DOM is slightly different but a lot more powerful.

$document = new DOMDocument();
$document->load($xmlFile);
$xpath = new DOMXpath($document);
$xpath->registerNamespace('a', 'http://www.w3.org/2005/Atom');

foreach ($xpath->evaluate('/a:feed/a:entry[position() < 3]') as $entry) {
  var_dump($xpath->evaluate('string(a:title)', $entry));
}

Output:

string(24) "Sharing the goals around"
string(34) "Kouyate inspires Hammers' comeback"

Xpath expression using with DOMXpath::evaluate() can return scalar values.

查看更多
登录 后发表回答