How to find element in specific context using DOMXPath in php?

2.1k views Asked by At

I'm trying to parse a xml file with information about some users. Something like this

users.xml

<?xml version="1.0" encoding="UTF-8"?>
<users>
    <user>
        <name>Alexander</name>
        <email>[email protected]</email>
    </user>
    <user>
        <name>Tyler</name>
        <email>[email protected]</email>
    </user>
</users>

I'm using DOMXpath to extract all users inside this xml, and all of this fields. In xml file, I have 15 users and when I search all fields about one user in this code

$username = $xpath->query ( '//email', $users->item(0) );

I got 15 length, instead of 1. I mean query is searching in all xml instead of looking for the actual user. What I'm doing wrong?

xml_query.php

$xml = new DOMDocument();
$xml->loadXML( $xml_content );
$xpath = new DOMXPath($xml);

$users = $xpath->query( '//user' );
var_dump( $users->item(0) );
$username = $xpath->query ( '//email', $users->item(0) );

var_dump( $username );
2

There are 2 answers

2
Mohammad On BEST ANSWER

Because //email select all elements in the document but you need to select elements within the current context. Remove // from query expression.

$username = $xpath->query ( 'email', $users->item(0) );

See result in demo

0
ThW On

A slash (/) at the start of an location path makes it relative to the document node. The context node will be ignored. If it is followed by another slash, it uses the descendant axis. //email is short for /descendant::email. The default axis is child.

$xml = <<<'XML'
<?xml version="1.0" encoding="UTF-8"?>
<users>
    <user>
        <name>Alexander</name>
        <email>[email protected]</email>
    </user>
    <user>
        <name>Tyler</name>
        <email>[email protected]</email>
    </user>
</users>
XML;

$document = new DOMDocument();
$document->loadXml($xml);
$xpath = new DOMXpath($document);
foreach ($xpath->evaluate('//user') as $user) {
  var_dump($xpath->evaluate('string(email)', $user));
}

Output:

string(18) "[email protected]"
string(14) "[email protected]"

DOMXpath::evaluate() can return node lists or scalars depending on the expression. It allows you to cast the nodes to string directly in the Xpath expression.

Xpath expression can contain conditions so to output the email by position you could do something like:

foreach ($xpath->evaluate('//user[2]') as $user) {
  var_dump($xpath->evaluate('string(email)', $user));
}

Output:

string(14) "[email protected]"