Используйте следующее выражение XPath 1.0 :
$v1/ancestor::*
[count(. | $v2/ancestor::*)
=
count($v2/ancestor::*)
]
[1]
, где $ v1 и $ v2 содержат два текстовых узла (если вы используете XPath вне XSLT, вам придется заменить $ v1 и $ v2 в вышеприведенном выражении на выражения XPath, которые выбирают каждый из этих двух текстовых узлов).
Объяснение :
Вышеупомянутое выражение XPath 1.0 находит пересечение двух наборов узлов : набор узлов всех предков элементов в $ v1 и набор узлов всех предков элементов в $ v2. Это делается с помощью так называемого метода Кайса для пересечения (после Майкла Кея, который открыл это в 2000 году). Используя метод пересечения Кейса, пересечение двух наборов узлов, $ ns1 и $ ns2, выбирается следующим выражением XPath :
$ns1[count(. | $ns2) = count($ns2)]
Затем из пересечения предков мы должны выбрать последний элемент . Однако , поскольку мы используем обратную ось (предок), требуемое положение узла должно быть обозначено как 1 .
Можно быстро проверить, что вышеприведенное выражение XPath действительно выбирает наименьшего общего предка , применив следующее преобразование:
<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>
при применении к первоначально предоставленному документу XML (исправлено в правильно сформированном XML):
<outer>
<main>
<a>
<b>sometext</b>
</a>
<c>
<d>sometext2</d>
</c>
</main>
</outer>
требуемый результат (имя элемента, который является наименьшим общим предком двух текстовых узлов) получается :
основной
Выражение XPath 2.0, которое выбирает наименьшего общего предка двух узлов, проще , поскольку в нем используется стандартный оператор XPath 2.0 "пересечение":
($v1/ancestor::* intersect $v2/ancestor::*)
[last()]