Use the following XPath 1.0 expression:
$v1/ancestor::*
[count(. | $v2/ancestor::*)
=
count($v2/ancestor::*)
]
[1]
where $v1 and $v2 hold the two text nodes (in case you use XPath not within XSLT, you will have to replace $v1 and $v2 in the above expression with the XPath expressions that select each one of these two text nodes).
Explanation:
The above XPath 1.0 expression finds the intersection of two node-sets: the node-set of all element ancestors of $v1 and the node-set of all element ancestors of $v2. This is done with the so called Kaysian method for intersection (after Michael Kay, who discovered this in 2000). Using the Kaysian method for intersection, the intersection of two nodesets, $ns1 and $ns2 is selected by the following XPath expression:
$ns1[count(. | $ns2) = count($ns2)]
Then, from the intersection of the ancestors we must select the last element. However, because we are using a reverse axis (ancestor), the required node position must be denoted as 1.
One can quickly verify that the above XPath expression really selects the lowest common ancestor, by applying the following transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:variable name="v1" select="/*/*/a/b/text()"/>
<xsl:variable name="v2" select="/*/*/c/d/text()"/>
<xsl:variable name="vCommonAncestor" select=
"$v1/ancestor::*
[count(. | $v2/ancestor::*)
=
count($v2/ancestor::*)
]
[1]"
/>
<xsl:template match="/">
<xsl:value-of select="name($vCommonAncestor)"/>
</xsl:template>
</xsl:stylesheet>
when applied on the originally-provided XML document (corrected to well-formed XML):
<outer>
<main>
<a>
<b>sometext</b>
</a>
<c>
<d>sometext2</d>
</c>
</main>
</outer>
the wanted result (the name of the element that is the lowest common ancestor of the two text nodes) is produced:
main
The XPath 2.0 expression that selects the lowest common ancestor of the two nodes is simpler, because it uses the standard XPath 2.0 operator "intersect":
($v1/ancestor::* intersect $v2/ancestor::*)
[last()]