4

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?

kjhughes
  • 106,133
  • 27
  • 181
  • 240
Jack Fleeting
  • 24,385
  • 6
  • 23
  • 45
  • 1
    Nice [mcve] and title! – kjhughes May 12 '20 at 21:19
  • 1
    @kjhughes Thank you, kind sir! – Jack Fleeting May 12 '20 at 21:32
  • I have the answer now, but I have to admit I'm perplexed as to why Michael Kay chose to close the question. I don't think the answer he refers to answers this question, especially since xslt isn't involved. Even if it did, closing this one, as opposed to just linking to that other question, seems somewhat harsh since other people out there may want to weigh in on the discussion. – Jack Fleeting May 13 '20 at 11:09
  • 1
    I can see how the other question/answer can be considered a duplicate, but up until now the other question wasn't even tagged xpath. This question is strictly XPath and, in my opinion, would be helpful to future visitors. Voting to reopen. – Daniel Haley May 15 '20 at 14:34

1 Answers1

3

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

kjhughes
  • 106,133
  • 27
  • 181
  • 240
  • 1
    Facepalm! I went thru this `*[]` vs `()[]` exercise 100 times, but apparently not when negation was involved... Thanks, for this. Now I can go on to the next problem! – Jack Fleeting May 12 '20 at 21:32