Using XPATH previous:: more like an array - xpath

I've got XML like this
<root>
...
<a>
<a>
<a>
<c>
...
It's very flat with LOTS of A elements and a few C elements. The A elements are sensor data and the last reading is bogus, I need the one before. So I'd like to use the C elements as a marker and each of A elements 2 before each C. So I'm trying out an XPATH like:
/root/c/preceding-sibling::a
but I'm getting all previous A elements, I was hoping for something a bit more direct such as:
/root/c/preceeding-sibling[-2]
which would just grab the 2nd sibling before C (no matter the type) I guess I'm asking for array like functionality on an XPATH so what ever I match I can ask for "the second element before that"
Is this possible?

You can
just grab the 2nd sibling before C (no matter the type)
with the XPath expression
/root/c/preceding-sibling::*[2]
The node count for preceding-sibling:: is going backwards. The node with the index [1] is the node before c and the node with the index [2] is the node before this - which is
the second element before that

Related

xpath: nested nodes with same name

Is it possible to write Xpath exression to find following nodes:
a/b/c
a/b/b/c
a/b/b/b/c
a/b/b/b/.../b/.../b/c
etc.
but not
a/b/b/e/b/c
?
Thanks for any advice
For the following input:
<root>
<a><b><c>1</c></b></a>
<a><b><b><c>2</c></b></b></a>
<a><b><b><b><c>3</c></b></b></b></a>
<a><b><b><e><b><c>4</c></b></e></b></b></a>
<a><b><b><e><b><b><c>5</c></b></b></e></b></b></a>
</root>
You can try the following expression:
//a[deep-equal(distinct-values(descendant::*[position()<last()]/name()), ('b'))]//c
How it works:
For any a the names of all but the last descendant are calculated and it is checked if the distinct list of that names matches the list containing just b. And for such an a the descendant c is one you want to select.
I tested it with the 2010 version of XMLSpy, which returns the c elements containing 1, 2 and 3. I think more modern tools will also work. But you need at least XPath 2.0 for this.
Question is not totally clear, but you can try to use below expression:
//a[.//b and not(.//e)]//c[not(.//*)]
This should allow to match cthat has no own child and is descendant of a, that has b descendant, but not e
UPDATE
You can try
//a[count(.//b)=count(.//*)-count(.//c)]//c[not(.//*)]
or
//a[not(.//*[not(self::b)]//c)]//c[not(.//*)]

Compare attribute of one element to attribute of another element

This seems like it should be easy, but I can never figure it out.
Presume I have the following document:
<data>
<a>
<b val="1"/>
</a>
<c val="1">
</data>
And assume that I am executing an XPath from the context of <b>. I need to check if there is an element c that has the same value as b.
Obviously, this doesn't work:
../a/c[#val=#val]
How to I get an XPath to remember its "current" context when traversing the tree?
Try the expression below. You'll notice that the current node is not lost since a predicate is used for finding the c node.
.[../../c/#val=#val]

Xpath expression to find non-child elements by attribute

here's a nice puzzle. Suppose we have this bit of code:
<page n="1">
<line n="3">...</line>
</page>
It is real easy to locate the line element "n=3" within the page element "n=1" with a simple xpath expression: xpath(//page[#n='1')/line[#n='3']). Great, beautiful, elegant.
Now suppose what we have is this encoding (folks familiar with the TEI will know where this is coming from).
<pb n="1"/>
(arbitrary amounts of stuff)
<lb n="3"/>
We want to find the lb element with n="3", which follows the pb element with n="1". But note -- this lb element could be almost anywhere following the pb: it may not be (and most likely is not) a sibling, but could be a child of a sibling of the pb, or of the pb's parent, etc etc etc.
So my question: how would you search for this lb element with n="3", which follows the pb element with n="1", with XPath?
Thanks in advance
Peter
Use:
//pb[#n='1']/following::lb[#n='2']
|
//pb[#n='1']/descendant::lb[#n='2']
This selects any lb element that follows the specified pb in document order -- even if the wanted lb element is a descendant of the pb element.
Do note that the following expression doesn't in general select all wanted lb elements (it fails to select any of these that are descendants of the pb element):
//pb[#n='1']/following::lb[#n='2']
Explanation:
As defined in the W3C XPath specification, the following:: and descendant:: axes are non-overlapping:
"the following axis contains all nodes in the same document as the
context node that are after the context node in document order,
excluding any descendants and excluding attribute nodes and namespace nodes"
That would be
//pb[#n=1]/following::lb[#n=3]

XPath: How do I select text() or a span element within a parent element

I have a parent element (font) and I would like to select all the child elements (direct descendants) that are either text() or span elements. How would I construct such an xpath?
If the current node is the font element, then something like this:
text()|span
otherwise you have to always combine with | the two complete XPath - the one for text and the one for span, e.g.:
font/text()|font/span
if the current node is just above font - or
//a[text()='View Larger Map']/../../../../div[contains(#class, 'paragraph')][3]/font/span|//a[text()='View Larger Map']/../../../../div[contains(#class, 'paragraph')][3]/font/text()
if starting from the root with some complex selection criteria.
If you have complex paths like the last one probably it is better to store a partial one in a variable - e.g. inside an XSLT:
<xsl:variable name="font" select="//a[text()='View Larger Map']/../../../../div[contains(#class, 'paragraph')][3]/font"/>
. . .
<xsl:for-each select="$font/span|$font/text()">
. . .
</xsl:for-each>
Another possibility is to do something like this:
//a[text()='View Larger Map']/../../../../div[contains(#class, 'paragraph')][3]/font/node()[name()='span' or name()='']
that works because name() returns an empty string for text() nodes - but I am not 100% sure that it works that way for all XPath processors, and it could match by mistake comment nodes.

Xpath to select only nodes where child elements exist?

This should be an easy one but it is giving me trouble. Given this structure:
<root>
<a>
<b/>
</a>
<a/>
</root>
I'm trying to formulate an xpath expression that gives only the non-empty "a" elements, i.e. the ones that have child elements. Therefore I want the first instance of "a" returned, but not the second.
So far I have "/root/a/self::*" but that is returning me both a's.
/root/a[count(*)>0]
will give any 'a' node with any kind of child node
/root/a[count(*)>0]
This one works
/root/a[*]
or even
//a[*]

Resources