Xpath 'or' behaves like union ('|') with xmllib2

I have XML documents, for example:

<rootelement> <myelement>test1</myelement> <myelement>test2</myelement> <myelement type='specific'>test3</myelement> </rootelement> 

I would like to get specific myelement and if which it does not represent , and then the first one. Therefore, I write:

 /rootelement/myelement[@type='specific' or position()=1] 

the XPath specification states of 'or expression', which:

The correct operand is not evaluated if the left operand is evaluated as true

The problem is that libxml2-2.6.26 seems to apply the union of both expressions, returning a "2 Node Set" (for example, using xmllint --shell ).

Is it libxml2, or am I doing something wrong?

+6
xpath union libxml2
source share
2 answers

Short answer: your selector does not express what you think it does.


The or operator is a union.

The part of the specification that you specified ("The correct operand is not evaluated ...") is part of the standard logic logic short circuit ..

That's why you get a 2 node set to enter your example: XPath looks at each myelement that is a child of rootelement , and applies the [@type='specific' or position()=1] to each of these nodes to determine, or does not match the selector.

  • <myelement>test1</myelement> does not match @type='specific' , but matches position()=1 , so it matches the entire selector.
  • <myelement>test2</myelement> does not match @type='specific' and also does not match position()=1 , so it does not match all selectors.
  • <myelement type='specific'>test3</myelement> matches @type='specific' (so XPath shouldn't check its position - it's the short-circuited part) so that it matches the entire selector.

The first and last <myelement> correspond to the entire selector, so it returns a set of 2 node.

The easiest way to select items the way you want is to do it in two steps. Here's the pseudo code (I don’t know in what context you are actually using XPath, and I'm not so good at writing XPath syntax selectors):

  • Select elements that match /rootelement/myelement[@type='specific']
  • If elements empty, select elements that matches /rootelement/myelement[position()=1]
+10
source share

@Matt Ball very well explained the cause of your problem.

Here is one XPath single-line set that selects exactly what you want :

 /*/myelement[@type='specific'] | /*[not(myelement[@type='specific'])]/myelement[1] 
+7
source share

All Articles