I have an XML fragment like
<main>
<sub1>
<element>
<sub2>
<element>
<sub3/>
</element>
</sub2>
</element>
</sub1>
<sub4>
<sub5>
<element>
<sub6>
<element>
<sub7/>
</element>
</sub6>
</element>
</sub5>
</sub4>
<sub8>
<element/>
</sub8>
</main>
If main is the current node, I can select all element nodes like this:
select=".//element"
This gives me 5 matches in this example. But how do I select only the first element node in the hierarchy, so the ones after sub1, sub5 and sub8? I need to ignore all elements that have another element between them and the original main node.
The number of sub nodes in between may be more than 1 or 2 as in the above example, and the main node in the example may be part of another element node above.
thanks
K
Use:
Here is a simple verification with XSLT:
When this transformation is applied on the provided XML document:
the Xpath expression is evaluated with the current node being the top node in the document, then the results of this evaluation are copied to the output, using a convenient to notice delimiter string:
Explanation:
The expression:
selects all descendat
elementelements of the current node the first of the two possible ancestors (upwards)mainorelementismain.That is, there isn’t an
elementthat is an ancestor of the selectedelementand happens (on the way upwards) before themainancestor.To understand why
last()is used as a predicate and not just[1], we need to remember that the nearest of two ancestors is the last of them in document order.Do note:
This XPath expression correctly selects all wanted
elementelements, even in the case when the current node has itself anelementancestor.An expression like the one proposed in the answer by Ian Roberts:
selects nothing in this case.
Update:
As noted by Michael Kay, if we further generalize this problem and allow other
mainelements to appear anywhere in the document, then the proposed expression in this answer may select “false-positives”.Here is an updated expression (no longer a pure/standalone XPath expression, as it uses the XSLT function
current()) that still produces the correct count in this situation: