Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XPath 1.0 exclusive or node-set expression

Tags:

xpath

What I need doesn't quite seem to match what other articles of a similar title are about.

I need, using Xpath 1, to be able to get node a, or node b, excusively, in that order. That is, node a if it exists, otherwise, node b.

an xpath expression such as :

expression | expression

will get me both in the case they both exist. that is not what I want.

I could go:

(expression | expression)[last()]

Which does in fact gget me what I need (in my case), but seems to be a bit inefficient, because it will evaluate both sides of the expression before the last result is selected. I was hoping for an expression that is going to stop working once the left side succeeds.

A more concrete example of XML

<one>
   <two>
     <three>hello</three>
     <four>bye</four>
   </two>
  <blahfive>again</blahfive>
</one>

and the xpath that works (but inefficient):

(/one/*[starts-with(local-name(.), 'blah')] | .)[last()]

To be clear, I would like to grab the immediate child node of 'one' which starts with 'blah'. However, if it doesn't exist, I would like only the current node. If the 'blah' node does exist, I do not want the current node.

Is there a more efficient way to achieve this?

like image 620
svaens Avatar asked Sep 26 '14 15:09

svaens


People also ask

What is node set in XPath?

A node set is a set of nodes. When you write an XPath expression to return one or more nodes, you call these nodes a node set. For example, if you use the following expression to return a node called title , you will have a set of nodes all called title (assuming there's more than one record).

What is the meaning of '/' in XPath?

Relative XPath It starts with the double forward slash (//), which means it can search the element anywhere at the webpage. You can start from the middle of the HTML DOM structure with no need to write a long XPath. Below is the example of a relative XPath expression of the same element shown in the below screen.

Which node types are valid in XPath?

In XPath, there are seven kinds of nodes: element, attribute, text, namespace, processing-instruction, comment, and document nodes. XML documents are treated as trees of nodes. The topmost element of the tree is called the root element.


1 Answers

I need, using Xpath 1, to be able to get node a, or node b, excusively, in that order. That is, node a if it exists, otherwise, node b.

an xpath expression such as :

expression | expression

will get me both in the case they both exist. that is not what I want.

I could go:

(expression | expression)[last()]

Which does in fact gget me what I need (in my case),

This statement is not true.

Here is an example. Let us have this XML document:

<one>
  <a/>
  <b/>
</one>

Expression1 is:

/*/a

Expression2 is:

/*/b

Your composite expression:

(Expression1 | Expression2)[last()]

when we substitute the two expressions above is:

(/*/a | /*/b)[last()]

And this expression actually selects b -- not a -- because b is the last of the two in document order.

Now, here is an expression that selects just a if it exists, and selects b only if a doesn't exist -- regardless of document order:

/*/a | /*/b[not(/*/a)]

When this expression is evaluated on the XML document above, it selects a, regardless of its document order -- try swapping in the XML document above the places of a and b to confirm that in both cases the element that is selected is a.

To summarize, one expression that selects the wanted node regardless of any document order is:

Expression1 | Expression2[not(Expression1)]

Let us apply this general expression in your case:

Expression1 is:

/one/*[starts-with(local-name(.), 'blah')] 

Expression2 is:

self::node()

The wanted expression (after substituting Expression1 and Expression2 in the above general expression) is:

   /one/*[starts-with(local-name(.), 'blah')] 
|  
   self::node()[not(/one/*[starts-with(local-name(.), 'blah')])]
like image 165
Dimitre Novatchev Avatar answered Oct 04 '22 17:10

Dimitre Novatchev