Подтвердить что ты не робот

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>
4b9b3361

Ответ 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>