XML tree traversal to get attribute value

This is my XML (snippet):

<?xml version="1.0" encoding="utf-8"?> <test> <body label_position="left"> <page number="1"> <itemset name=""> <item name="" id="1" /> <item name="" id="2" label_position="right" /> </itemset> </page> </body> </test> 

What I want to do:
Is <item id="1"> the label_position attribute label_position ?
(1) If so, return it.
(2) If not, check the parent node for this attribute.
(3) If the parent has an attribute, return it.
(4) If not, go to step (2). <body> is the “oldest” node to check.

What I tried:
I use simplexml and xpath , and I tried to select <T26> and all its ancestors, and then move up the tree and stop with the first appearance of label_position .

 $xml->xpath("//item[@id='1']::ancestors-or-self/@label_position"); 

produced an invalid expression .

(1) How can I make this expression work?
(2) Can this be done using "xpath-only" - without crossing the tree and searching?

EDIT: thanks to choroba and dirkk, I was able to put it together:

 $test = (string)array_reverse($xml->xpath("//item[@id='2']/ancestor-or-self::*/@label_position"))[0]; 

Explanation: if both the <item> and <body> attributes have an attribute, xpath will return an array with both values, <body> first. In this case, I want to get <item> , therefore array_reverse() .

see its working: http://codepad.viper-7.com/jDPIde

+4
source share
2 answers

You should be more careful about XPath syntax. The correct form of your expression

 //item[@id='1']/ancestor-or-self::*/@label_position 

You cannot use :: after the predicate, and you must specify :: after the axis.

+2
source

Your XPath is not valid. The axis step must be before the element name, for example. something like axis::node , not the other way around.

The following should work. Your logic is already xpath-only, and navigate the tree perfectly.

 (//item[@id='1']/ancestor-or-self::*/@label_position)[last()] 
+2
source

Source: https://habr.com/ru/post/1490292/


All Articles