Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Negation in xpath - hilarity ensues

Tags:

xml

xpath

Recently I've been running into xpath problems on several fronts (fodder for future questions probably); but the more I looked into these, the more convinced I became that part of the problem is that I don't really get how negation works in xpath.

Let's assume we're given this:

<teams>
   <team>
     <title>My Own Baseball Team</title>
       <player name="Abbot" position="first_base" />
       <player name="Costello" position="second_base"  />
     <location>Here</location>
   </team>
</teams>

Let's run a few sanity checks:

count(//*)

Output is 6. Correct.

How many players are there?

count(//*[name()="player"])

Output is 2. Correct again.

How many non-players are there? To be on the safe side, let's ask it 3 different ways:

count(//*[not(name()="player")])
count(//*[name()!="player"])
count(//*[name() ne "player"])

Output is always 4; makes sense - 6 elements, 2 players - so 4 non-players.

Just so get our bearings, who are the non-players?

//*[name() != "player"]/name()

Output:

teams
team
title
location

Now we know. So let's borrow a question from the old Abbot & Costello routine. Among the players, who's on second?

//*[name()= "player"][2]/@name

Output is Costello. Right again.

Among the 4 non-players, who's on third?

//*[name() != "player"][3]

No match! Same answer regardless of how your form the negation.

What? We determined before that we have 4 non-players - how come nobody's on third? Backtracking, let's see who's on first, explicitly this time:

//*[not(name() = "player")][position()=1]/name()

Output:

teams
team
title

What again? Three of the 4 non-players are on first? In that case, the 4th non-player must be on second, right?

//*[name() ne "player"][position()=2]/name()

Output is location. That's better, isn't it? So location is last among non-players?

//*[name() != "player"][position()=last()]/name()

Output is more Alice in Wonderland than A&C:

teams
team
location

So this is where I give up - it no longer makes any sense to me.

Why is this happening? How would you formulate the query such the the answer to the question "in what non-player position can location be found" is 4?

like image 399
Jack Fleeting Avatar asked Oct 17 '25 08:10

Jack Fleeting


1 Answers

The trouble begins with a common misunderstanding of what this XPath means:

//*[name() != "player"][3]

It does not mean, select the third non-player; it means, select from the non-players those that are the third child of its respective parent. (There are no such non-player elements, as you've discovered.)

If you want to select the third of all non-player elements, use

( one of your non-player XPaths, within parenthesis )[3]

See also

  • How to select first element via XPath?
  • XPath - (//first//*)[1] vs //first//*[1]
like image 74
kjhughes Avatar answered Oct 19 '25 22:10

kjhughes



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!