Xpath Axis, Get All Following Nodes Until

XPath axis, get all following nodes until

Use:

(//h2[. = 'Foo bar'])[1]/following-sibling::p
[1 = count(preceding-sibling::h2[1] | (//h2[. = 'Foo bar'])[1])]

In case it is guaranteed that every h2 has a distinct value, this may be simplified to:

//h2[. = 'Foo bar']/following-sibling::p
[1 = count(preceding-sibling::h2[1] | ../h2[. = 'Foo bar'])]

This means: Select all p elements that are following siblings of the h2 (first or only one in the document) whose string value is 'Foo bar' and also the first preceding sibling h2 for all these p elements is exactly the h2(first or only one in the document) whose string value is'Foo bar'`.

Here we use a method of finding whether two nodes are identical:

count($n1 | $n2) = 1

is true() exactly when the nodes $n1 and $n2 are the same node.

This expression can be generalized:

$x/following-sibling::p
[1 = count(preceding-sibling::node()[name() = name($x)][1] | $x)]

selects all "immediate following siblings" of any node specified by $x.

XPath : select all following siblings until another sibling

You could do it this way:


../node[not(text()) and preceding-sibling::node[@id][1][@id='1']]

where '1' is the id of the current node (generate the expression dynamically).

The expression says:

  • from the current context go to the parent
  • select those child nodes that
  • have no text and
  • from all "preceding sibling nodes that have an id" the first one must have an id of 1

If you are in XSLT you can select from the following-sibling axis because you can use the current() function:

<!-- the for-each is merely to switch the current node -->
<xsl:for-each select="node[@id='1']">
<xsl:copy-of select="
following-sibling::node[
not(text()) and
generate-id(preceding-sibling::node[@id][1])
=
generate-id(current())
]
" />
</xsl:for-each>

or simpler (and more efficient) with a key:

<xsl:key 
name="kNode"
match="node[not(text())]"
use="generate-id(preceding-sibling::node[@id][1])"
/>

<xsl:copy-of select="key('kNode', generate-id(node[@id='1']))" />

Xpath to find all elements following specific node

*[preceding-sibling::h2] indicates all elements after h2. So after adding contains in the selector it goes like this

//div[@id='myDiv']/*[preceding-sibling::h2[contains(text(),'Locator')]]

See Demo Here. Click The Test button you will get the result

How XPath axes preceding:: and following:: work?

This isn't a matter of what you believe, it's a matter of what the specs say. The latest (3.1) version of XPath defines these axes at

https://www.w3.org/TR/xpath-31/#axes

as follows:

the following axis contains all nodes that are descendants of the root
of the tree in which the context node is found, are not descendants of
the context node, and occur after the context node in document order

the preceding axis contains all nodes that are descendants of the root
of the tree in which the context node is found, are not ancestors of
the context node, and occur before the context node in document order

The definition has not changed over time.

Sadly, it's not unusual for books to simplify. On the other hand, you may have missed something in the books you were reading, you haven't cited them specifically, so I can't tell.

XPath: Select following siblings until certain class

Good question!

The following expression will give you 1..2, 3..5 or 6..7, depending on input X + 1, where X is the set you want (2 gives 1-2, 3 gives 3-.5 etc). In the example, I select the third set, hence it has [4]:

/table/tr[1]
/td[not(@class = 'foo')]
[
generate-id(../td[@class='foo'][4])
= generate-id(
preceding-sibling::td[@class='foo'][1]
/following-sibling::td[@class='foo'][1])
]

The beauty of this expression (imnsho) is that you can index by the given set (as opposed to index by relative position) and that is has only one place where you need to update the expression. If you want the sixth set, just type [7].

This expression works for any situation where you have siblings where you need the siblings between any two nodes of the same requirement (@class = 'foo'). I'll update with an explanation.

Replace the [4] in the expression with whatever set you need, plus 1. In oXygen, the above expression shows me the following selection:

Sample Image

Explanation

/table/tr[1]

Selects the first tr.

/td[not(@class = 'foo')]

Selects any td not foo

generate-id(../td[@class='foo'][4])

Gets the identity of the xth foo, in this case, this selects empty, and returns empty. In all other cases, it will return the identity of the next foo that we are interested in.

generate-id(
preceding-sibling::td[@class='foo'][1]
/following-sibling::td[@class='foo'][1])

Gets the identity of the first previous foo (counting backward from any non-foo element) and from there, the first following foo. In the case of node 7, this returns the identity of nothingness, resulting in true for our example case of [4]. In the case of node 3, this will result in c, which is not equal to nothingness, resulting in false.

If the example would have value [2], this last bit would return node b for nodes 1 and 2, which is equal to the identity of ../td[@class='foo'][2], returning true. For nodes 4 and 7 etc, this will return false.

Update, alternative #1

We can replace the generate-id function with a count-preceding-sibling function. Since the count of the siblings before the two foo nodes is different for each, this works as an alternative for generate-id.

By now it starts to grow just as wieldy as GSerg's answer, though:

/table/tr[1]
/td[not(@class = 'foo')]
[
count(../td[@class='foo'][4]/preceding-sibling::*)
= count(
preceding-sibling::td[@class='foo'][1]
/following-sibling::td[@class='foo'][1]/preceding-sibling::*)
]

The same "indexing" method applies. Where I write [4] above, replace it with the nth + 1 of the intersection position you are interested in.

Select all nodes until a specific given node/tag

Update: much simpler solution

There is a prerequisite in your situation, that is that the anchor item always is the first preceding sibling with a certain property. Because of that, here's a much simpler way of writing the below complex expression:

/div/dl/dd[preceding-sibling::dt[1][. = 'Names']]

In other words:

  • select any dd
  • that has a first preceding sibling dt (the preceding sibling axis counts backwards)
  • that itself has a value of "Names"

As can be seen in the following screenshot from oXygen, it selects the nodes you wanted to select (and if you change "Names" to "Status" or "Another Field", it will select only the following ones before the next dt also).

screenshot from oXygen of selected nodes

Original complex solution (leaving in for reference)

This is far easier in XPath 2.0, but let's assume you can only use XPath 1.0. The trick is to count the number of preceding siblings from your anchor element (the one with "Names" in it), and disregard any that have the wrong count (i.e., when we cross over <dt>Status</dt>, the number of preceding siblings has increased).

For XPath 1.0, remove the comments between (: and :) (in XPath, whitespace is insignificant, you can make it a multiline XPath for readability, but in 1.0, comments are not possible)

/div/dl/dd

(: any dd having a dt before it with "Names" :)
[preceding-sibling::dt[. = 'Names']]

(: count the preceding siblings up to dt with "Names", add one to include 'self' :)
[count(preceding-sibling::dt[. = 'Names']/preceding-sibling::dt) + 1
=

(: compare with count of all preceding siblings :)
count(preceding-sibling::dt)]

As a one-liner:

/div/dl/dd[preceding-sibling::dt[. = 'Names']][count(preceding-sibling::dt[. = 'Names']/preceding-sibling::dt) + 1 = count(preceding-sibling::dt)]

Xpath: select preceding and following sibling

This XPath will do that work:

//*[self::p or self::*/li][preceding-sibling::h2 and following-sibling::h2]

Explanations:

[preceding-sibling::h2 and following-sibling::h2] is limiting the sibling elements between the h2 tags.

//*[self::p or self::*/li] selecting p tag nodes or li sub-nodes of any kind of parent tag.

It works, you can check it here or with any similar XPath online validator.

XPath - All following siblings except first specific elements

Use:

 /table[@id='target']/following-sibling::*[not(self::table) and not(self::ol)] 
|
/table[@id='target']/following-sibling::table[position() > 1]
|
/table[@id='target']/following-sibling::ol[position() > 1]

This selects all the following siblings of the table that are not table and are not ol and all the following table siblings with position 2 or greater and all the following ol siblings with position 2 or greater.

Which is exactly what you want: all following siblings with the exception of the first table following sibling and the first ol following siblings.

This is pure XPath 1.0 and not using any XSLT functions.



Related Topics



Leave a reply



Submit