XPath 1.0 ближайший предшественник и/или предка node с атрибутом в дереве XML
Вот три дерева XML
(1)
<?xml version="1.0" encoding="UTF-8"?>
<content>
<section id="1">
<section id="2"/>
<section id="3"/>
<section id="9"/>
</section>
<section id="4">
<section id="5">
<section>
<bookmark/> <!-- here the bookmark-->
<section id="6">
<section id="7">
<section id="8"/>
</section>
</section>
</section>
</section>
</section>
</content>
(2)
<?xml version="1.0" encoding="UTF-8"?>
<content>
<section id="1"/>
<section id="2">
<section id="9">
<section id="10">
<section id="11"/>
</section>
</section>
<section>
<section id="4">
<section id="5"/>
</section>
</section>
<section/>
<bookmark/> <!-- here the bookmark-->
<section id="6">
<section id="7">
<section id="8"/>
</section>
</section>
</section>
</content>
Желаемый результат в обоих случаях равен id 5.
С XSLT 1.0 и XPath 1.0 я могу либо получить предка из (1)
<xsl:value-of select="//bookmark/ancestor::*[@id][1]/@id"/>
или предыдущий node из (2)
<xsl:value-of select="//bookmark/preceding::*[@id][1]/@id"/>
Как мне получить ближайший предка или, предшествующий node с идентификатором из моей закладки?
Мне нужно одно xsl: значение, которое соответствует обоим случаям. Спасибо.
EDIT:
Решение также должно охватывать эту структуру. Желаемый id все еще 5.
(3)
<?xml version="1.0" encoding="UTF-8"?>
<content>
<section id="1">
<section id="2"/>
<section id="3"/>
<section id="9"/>
</section>
<section id="4">
<section>
<section id="10"/>
<section id="5"/>
<section>
<bookmark/> <!-- here the bookmark-->
<section id="6">
<section id="7">
<section id="8"/>
</section>
</section>
</section>
</section>
</section>
</content>
Ответы
Ответ 1
Использование
(//bookmark/ancestor::*[@id][1]/@id
|
//bookmark/preceding::*[@id][1]/@id
)
[last()]
Проверка. Используя XSLT в качестве хоста XPath, выполните следующее преобразование:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:value-of select=
"(//bookmark/ancestor::*[@id][1]/@id
|
//bookmark/preceding::*[@id][1]/@id
)
[last()]"/>
</xsl:template>
</xsl:stylesheet>
, если применяется к любому из предоставленных трех документов XML, дает желаемый, правильный результат:
5
Я сильно рекомендую использовать XPath Visualizer для игры с /learning XPath.
Ответ 2
Попробуйте:
<xsl:value-of
select="//bookmark/ancestor::*[1]/descendant-or-self::*[last()-1]/@id"/>
Он возвращает 5
для обоих документов XML.
EDIT:
В таких условиях вы можете использовать простой xsl:choose
:
<xsl:variable name="lastSibling"
select="//bookmark/preceding-sibling::*[1]"/>
<xsl:choose>
<xsl:when test="$lastSibling">
<xsl:value-of
select="$lastSibling/descendant-or-self::*[last()]/@id"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="//bookmark/ancestor::*[@id][1]/@id"/>
</xsl:otherwise>
</xsl:choose>
Другое общее решение:
<xsl:for-each
select="//section[following::bookmark or descendant::bookmark][@id]">
<xsl:if test="position() = last()">
<xsl:value-of select="./@id"/>
</xsl:if>
</xsl:for-each>