Use xpath to select a node which has a greatest value for an attribute

时间:2016-02-12 20:03:28

标签: xml xpath

Given an xml document like this:

<node>
  <node text="text1" x=400 y=400></node>
</node>
<node>
  <node text="text1" x=400 y=900></node>
</node>
<node>
  <node text="text2" x=400 y=1000></node>
</node>
... etc ...

How would I write an xpath query to select the node where the text attribute is "text1" with the greatest value for y (in this case 900)? I don't know exactly what the y value will be, but I do know that the node I am always supposed to be selecting is the one with the higher y value, and that the two nodes will not be siblings. Aside from the y value, the two nodes I'm trying to differentiate between will have all attributes equal.

1 个答案:

答案 0 :(得分:3)

Your input document is not well-formed. It lacks a single outermost (root) element and some of the attribute values have no quotes. Given a well-formed input document like this:

<root>
<node>
  <node text="text1" x="400" y="400"></node>
</node>
<node>
  <node text="text1" x="400" y="900"></node>
</node>
<node>
  <node text="text2" x="400" y="1000"></node>
</node>
</root>

The following XPath expression:

//node[@text = 'text1' and not(@y < preceding::node[@text = 'text1']/@y or @y < following::node[@text = 'text1']/@y)]

finds the node element(s) that has text1 and the highest value in @y. In this case, the only result is

<node text="text1" x="400" y="900"/>

In more detail, the path expression means

//node                                              select all `node` elements, anywhere in
                                                    the document
[@text = 'text1'                                    but only if its `text` attribute is
                                                    equal to "text1"
and not(@y < preceding::node[@text = 'text1']/@y    and only if the value of its `y`
                                                    attribute is not lower than the value
                                                    of an `y` attribute of another `node`
                                                    element of the same sort that precedes
                                                    the current one
or @y < following::node[@text = 'text1']/@y)]       and only if the value if its `y`
                                                    attribute is not lower than the value
                                                    of an `y` attribute of another `node`
                                                    element of the same sort that follows
                                                    the current one

EDIT: Alternatively, use

//node[@text="text1" and not(//node[@text="text1"]/@y > @y)]

as splash58 has suggested. The semantics of this expression are very similar to the one above.