Garret Wilson
Garret Wilson

Reputation: 21486

Limit XPath matches (or choose match index) at location step

I want to limit the number of matches, or better yet, choose which indexed match, is made at a location step in XPath.

I know that if I have the following document:

<blah>
  <foo>
    <bar>
      <target>one</target>
      <target>two</target>
    </bar>
    <bar>
      <target>three</target>
      <target>four</target>
    </bar>
  </foo>
</blah>

I can limit the expression to only include the first <bar> directly under <foo> like the following, in order to only return <target>one</target> and <target>two</target>:

//foo/bar[1]/target

But now what if I have this:

<blah>
  <foo>
    <blah>
      <bar>
        <target>one</target>
        <target>two</target>
      </bar>
    </blah>
    <blah>
      <bar>
        <target>three</target>
        <target>four</target>
      </bar>
    </blah>
  </foo>
</blah>

I still only want the expression to yield <target>one</target> and <target>two</target>. But the following doesn't work; it will actually match <bar> under each <foo><blah>

//foo//bar[1]/target

Apparently there is a subtle distinction: bar[1] means "the first child of some element", not "the first element that matches this location step in the path expression".

How can I limit the expression (without naming the <blah> elements, because they can vary) so that when it reaches the //foo//bar location it only retains the first match of <bar> before it continues matching the expression?

Upvotes: 1

Views: 64

Answers (1)

Garret Wilson
Garret Wilson

Reputation: 21486

Ah, with just a few more seconds of thought I found the answer! (Actually forming the question helped me discover the solution.)

The secret is simply to use parentheses:

(//foo//bar)[1]/target

If I'm understanding it correctly, the expression //foo//bar[1] says, "for every … <bar> you match, only take the first one of them for the parent", so if there are multiple "first-bars", it will take each of the "first-bars".

Instead the expression (//foo//bar)[1] says "once you match … <bar>, only take the first matching one; then evaluate the rest of the path from that one matching <bar>.

Whew! I wish every road bump could be this simple to get past.

Upvotes: 1

Related Questions