craig
craig

Reputation: 26272

Xpath: first preceding and following sibling

XML:

<body>
    <h2><font style="font-weight: bold">Baz</font></h2>
    <p><img title="image" /></p>
    <p>Baz 0 with an <a href="http://">anchor</a> element.</p>
    <p>Baz 1 with an <a href="http://">anchor</a> element.</p>
    <hr />
    <h2><font style="font-weight: bold">People</font></h2>
    <ul>
        <li>People 0 with <a href="http://" >an anchor</a> element.</li>
        <li>People 1 with an <a href="http://" >an anchor</a> element.</li>
    </ul>
    <hr/>
    <h2><font style="font-weight: bold">Sales</font></h2>
    <ul>
        <li>List item 2 with an <a href="http://" >an anchor</a> element.</li>
        <li>List item 3 with an <a href="http://" >an anchor</a> element.</li>
        <li>List item 4 without an anchor element.</li>
    </ul>
    <hr />
    <h2><font style="font-weight: bold">Sales</font></h2>
    <p><img title="image" /></p>
    <p>sales 0 with an <a href="http://">anchor</a> element.</p>
    <p>sales 1 with an <a href="http://">anchor</a> element.</p>
    <hr />
    <h2><font style="font-weight: bold">Foo</font></h2>
    <ul>
        <li>Foo 0 with <a href="http://" >an anchor</a> element.</li>
        <li>Foo 1 with an <a href="http://" >an anchor</a> element.</li>
    </ul>
    <hr />
    <h2><font style="font-weight: bold">Bar</font></h2>
    <p><img title="image" /></p>
    <p>bar 0 with an <a href="http://">anchor</a> element.</p>
    <p>bar 1 with an <a href="http://">anchor</a> element.</p>
    <hr />
</body>

This xpath: //p[a and preceding-sibling::h2[font[text()='Sales']][1] and following-sibling::hr[1]]

Returns:

<p>sales 0 with an <a href="http://">anchor</a> element.</p>
<p>sales 1 with an <a href="http://">anchor</a> element.</p>
<p>bar 0 with an <a href="http://">anchor</a> element.</p>
<p>bar 1 with an <a href="http://">anchor</a> element.</p>

Desired p:

<p>sales 0 with an <a href="http://">anchor</a> element.</p>
<p>sales 1 with an <a href="http://">anchor</a> element.</p>

Desired li:

<li>List item 2 with an <a href="http://" >an anchor</a> element.</li>
<li>List item 3 with an <a href="http://" >an anchor</a> element.</li>

What am I missing?

How would I alter the xpath to include li/[a] in the same manner that I'm including p/[a]? The preceding/following-sibling wouldn't work with li.

Upvotes: 3

Views: 3029

Answers (1)

Daniel Haley
Daniel Haley

Reputation: 52888

You should just need to specify that it's the first preceding-sibling h2:

preceding-sibling::h2[1]

updated xpath (I also simplified the test of Sales):

//p[a and preceding-sibling::h2[1][.='Sales'] and following-sibling::hr]

Also, if you need to be certain that the first following-sibling that isn't p is hr, you can try this...

//p[a and preceding-sibling::h2[1][.='Sales'] and following-sibling::*[not(self::p)][1][self::hr]]

If you're trying to also select li in addition to p, you could update the xpath to use preceding:: and following::, but you'd have to account for any elements that might appear as children of p like a, span, etc...

//*[self::p or self::li][a and preceding::h2[1][.='Sales'] and following::*[not(self::p) and not(self::li) and not(self::a)][1][self::hr]]

This would select the following from your sample XML...

<li>List item 2 with an <a href="http://" >an anchor</a> element.</li>
<li>List item 3 with an <a href="http://" >an anchor</a> element.</li>
<p>sales 0 with an <a href="http://">anchor</a> element.</p>
<p>sales 1 with an <a href="http://">anchor</a> element.</p>

However, I'd recommend a second xpath to target li specifically...

//li[a and preceding::h2[1][.='Sales'] and ../following-sibling::*[1][self::hr]]

Upvotes: 3

Related Questions