0

I am trying to query an XML file using XPath. But as return I get nothing. I think I formatted the query false.

XML

<subject id="Tom">
   <relation unit="ITSupport" role="ITSupporter" />
  </subject>

PHP

$xpath = new DOMXpath($doc);
            $role = 'ITSupporter';
           $elements = $xpath-> query("//subject/@id[../relation/@role='".$role."']");              
           foreach ($elements as $element) {    
              $name = $element -> nodeValue;
              $arr[$i] = $name;
              $i = $i + 1;
           }    

How can I get the id TOM? I want to save it to for example $var

3 Answers 3

1

Building up the Xpath expression:

  • Fetch any subject element
    //subject
  • ... with a child element relation
    //subject[relation]
  • ... that has a role attribute with the given text
    //subject[relation/@role="ITSupporter"]
  • ... and get the @id attribute of subject
    //subject[relation/@role="ITSupporter"]/@id

Additionally the source could be cleaned up. PHP arrays can use the $array[] syntax to push new elements into them.

Put together:

$xml = <<<'XML'
<subject id="Tom">
  <relation unit="ITSupport" role="ITSupporter" />
</subject>
XML;

$role = 'ITSupporter';

$document = new DOMDocument();
$document->loadXML($xml);
$xpath = new DOMXpath($document);

$ids = [];
foreach ($xpath->evaluate("//subject[relation/@role='".$role."']/@id") as $idAttribute) {
  $ids[] = $idAttribute->value;
}
var_dump($ids);

Output:

array(1) { 
  [0]=> 
  string(3) "Tom" 
}

If you expect only a single result you can cast the it in Xpath:

$id = $xpath->evaluate(
  "string(//subject[relation/@role='".$role."']/@id)"
);
var_dump($id);

Output:

string(3) "Tom"

XML Namespaces

Looking at the example posted in the comment your XML uses the namespace http://cpee.org/ns/organisation/1.0 without a prefix. The XML parser will resolve it so you can read the nodes as {http://cpee.org/ns/organisation/1.0}subject. Here are 3 examples that all resolve to this:

  • <subject xmlns="http://cpee.org/ns/organisation/1.0"/>
  • <cpee:subject xmlns:cpee="http://cpee.org/ns/organisation/1.0"/>
  • <c:subject xmlns:c="http://cpee.org/ns/organisation/1.0"/>

The same has to happen for the Xpath expression. However Xpath does not have a default namespace. You need to register an use an prefix of your choosing. This allows the Xpath engine to resolve something like //org:subject to //{http://cpee.org/ns/organisation/1.0}subject.

The PHP does not need to change much:

$xml = <<<'XML'
<subject id="Tom" xmlns="http://cpee.org/ns/organisation/1.0">
  <relation unit="ITSupport" role="ITSupporter" />
</subject>
XML;

$role = 'ITSupporter';

$document = new DOMDocument();
$document->loadXML($xml);
$xpath = new DOMXpath($document);
// register a prefix for the namespace
$xpath->registerNamespace('org', 'http://cpee.org/ns/organisation/1.0');

$ids = [];
// address the elements using the registered prefix
$idAttributes = $xpath->evaluate("//org:subject[org:relation/@role='".$role."']/@id");
foreach ($idAttributes as $idAttribute) {
  $ids[] = $idAttribute->value;
}
var_dump($ids);
Sign up to request clarification or add additional context in comments.

6 Comments

as a result I get: array(0) { } :(
Then you have a different XML - maybe with a namespace. Try my example without modifying it (3v4l.org/oJLGG).
Dear @ThW, your code is working! You have been right. But this is the XML file that I have to query: 3v4l.org/uKTS7. Shouldn't it be the same? :S I want to query all IDs (names) with the ID (for example ITSupporter)
No, your XML uses a namespace. //subject looks for an element in the "empty" namespace. You could think of it as <subject xmlns=""/>. I added some explanation and a modified example to the answer.
Dear @ThW, I tried your example, it works only when I copy paste it. But when I read the XML code from a file, I get null again. Can you help me please, I really don't get it, what am I doing wrong. I read the XML with: > $xml = 'organisation.xml'; > $document = new DOMDocument(); > $document->loadXML($xml); > $xpath = new DOMXpath($document);
|
1

Try this XPath

//subject[relation/@role='".$role."']/@id

You were applying the predicate on the id attribute and not on the subject element.

2 Comments

Hmm I still get nothing back. :( { "taskname": "Read order", "employee": "leer", "role": "ITSupporter", "form": "form1.html", "done": 0 }
Under Employee should be the Name. But I only get the initial value of my var. The query doesn't save anything
0

Getting element by id is the same as doing by $role contents. So, like the followings; $xpath->query("//*[@id='$id']")->item(0); In other words, @id should be in '[' bracket.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.