XSLT исключает один конкретный узел из Select

Я искал ответ на этот вопрос все выше и ниже и пробовал сотни вариантов, но ничего не помогло.

Я пытаюсь обработать все узлы простого XML-документа, КРОМЕ для самого первого <title> узел. По сути, я пытаюсь найти инструкцию xslt, которая выполняет точное ОБРАТНОЕ из

<xsl:apply-templates select="/topic/title"/>

Вот мой исходный XML:

<topic>
    <title>Name of the Document</title>
    <p>Document body</p>
    <topic>
        <title>First Document Subtopic</title>
        <p>Body text for first document subtopic</p>
    </topic>
    <p>Body text continued for document</p>
    <topic>
        <title>Second Document Subtopic</title>
        <p>Body text for document subtopic</p>
        <topic>
            <title>First Document Sub-subtopic</title>
            <p>Body text for first document sub-subtopic</p>
        </topic>
    </topic>
    <p>Body text continued a second time for document</p>
</topic>

и вот мой XSLT (с тем, что я изначально думал, должно работать при вызове apply-templates):

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output omit-xml-declaration="yes" encoding="UTF-8" method="xml"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="/">
        <BOX>
            <TEXTFLOW>
                <xsl:apply-templates select="*[not(/topic/title)]"/>
            </TEXTFLOW>
        </BOX>
    </xsl:template>

    <xsl:template match="topic">
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="title">
        <PARA STYLE="Title"/>
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="p">
        <PARA STYLE="BodyText"/>
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="text()">
        <FORMAT>
            <xsl:value-of select="."/>
        </FORMAT>
    </xsl:template>
</xsl:stylesheet>

и вот что я хочу увидеть (обратите внимание на отсутствие первого <title> иметь значение):

<BOX>
    <TEXTFLOW>
        <PARA STYLE="BodyText"/>
        <FORMAT>Document body</FORMAT>
        <PARA STYLE="Title"/>
        <FORMAT>First Document Subtopic</FORMAT>
        <PARA STYLE="BodyText"/>
        <FORMAT>Body text for first document subtopic</FORMAT>
        <PARA STYLE="BodyText"/>
        <FORMAT>Body text continued for document</FORMAT>
        <PARA STYLE="Title"/>
        <FORMAT>Second Document Subtopic</FORMAT>
        <PARA STYLE="BodyText"/>
        <FORMAT>Body text for document subtopic</FORMAT>
        <PARA STYLE="Title"/>
        <FORMAT>First Document Sub-subtopic</FORMAT>
        <PARA STYLE="BodyText"/>
        <FORMAT>Body text for first document sub-subtopic</FORMAT>
        <PARA STYLE="BodyText"/>
        <FORMAT>Body text continued a second time for document</FORMAT>
    </TEXTFLOW>
</BOX>

Какое выражение выбора я могу использовать для этого?

2 ответа

Решение

Ну, вы можете просто использовать этот XPath в своем корневом шаблоне:

select=".//*[generate-id() != generate-id(/topic/title)]"

Вам нужно изменить свой шаблон для topic чтобы:

<xsl:template match="topic" />

Проблема в том, что ваш текущий выбор определяет только непосредственных потомков, которые должны быть обработаны - из корневой заметки (/) это на самом деле только верхний уровень topic элемент. .//* в XPath выше указаны все потомки независимо от уровня, а предикат явно исключает первый /topic/title элемент.

Вы должны убедиться, что ваш topic элементы НЕ обрабатывают своих дочерних элементов, поскольку их дочерние элементы уже обрабатываются инструкцией select в корневом шаблоне.

Так легко, как это:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="/">
        <BOX>
            <TEXTFLOW>
                <xsl:apply-templates/>
            </TEXTFLOW>
        </BOX>
    </xsl:template>

    <xsl:template match="/topic/title"/>

    <xsl:template match="title">
        <PARA STYLE="Title"/>
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="p">
        <PARA STYLE="BodyText"/>
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="text()">
        <FORMAT>
            <xsl:value-of select="."/>
        </FORMAT>
    </xsl:template>
</xsl:stylesheet>

Когда это преобразование применяется к предоставленному документу XML:

<topic>
    <title>Name of the Document</title>
    <p>Document body</p>
    <topic>
        <title>First Document Subtopic</title>
        <p>Body text for first document subtopic</p>
    </topic>
    <p>Body text continued for document</p>
    <topic>
        <title>Second Document Subtopic</title>
        <p>Body text for document subtopic</p>
        <topic>
            <title>First Document Sub-subtopic</title>
            <p>Body text for first document sub-subtopic</p>
        </topic>
    </topic>
    <p>Body text continued a second time for document</p>
</topic>

желаемый, правильный результат получается:

<BOX>
   <TEXTFLOW>
      <PARA STYLE="BodyText"/>
      <FORMAT>Document body</FORMAT>
      <PARA STYLE="Title"/>
      <FORMAT>First Document Subtopic</FORMAT>
      <PARA STYLE="BodyText"/>
      <FORMAT>Body text for first document subtopic</FORMAT>
      <PARA STYLE="BodyText"/>
      <FORMAT>Body text continued for document</FORMAT>
      <PARA STYLE="Title"/>
      <FORMAT>Second Document Subtopic</FORMAT>
      <PARA STYLE="BodyText"/>
      <FORMAT>Body text for document subtopic</FORMAT>
      <PARA STYLE="Title"/>
      <FORMAT>First Document Sub-subtopic</FORMAT>
      <PARA STYLE="BodyText"/>
      <FORMAT>Body text for first document sub-subtopic</FORMAT>
      <PARA STYLE="BodyText"/>
      <FORMAT>Body text continued a second time for document</FORMAT>
   </TEXTFLOW>
</BOX>

Пояснение:

Чтобы исключить требуемый элемент (ы) из обработки, мы используем шаблон, который соответствует им и не имеет тела:

    <xsl:template match="/topic/title"/>

II. Обновить

Следующее альтернативное решение не рекомендуется, поскольку оно является примером "стиля вытягивания" и не так гибко, элегантно и легко обслуживаемо, как решение "стиля пуша", описанное выше, однако ОП настаивает на том, что ему нужно такое неполноценное решение:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="/">
      <BOX>
       <TEXTFLOW>
        <xsl:apply-templates
        select="*/*[not(self::title)]"/>
       </TEXTFLOW>
      </BOX>
    </xsl:template>

    <xsl:template match="title">
        <PARA STYLE="Title"/>
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="p">
        <PARA STYLE="BodyText"/>
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="text()">
        <FORMAT>
            <xsl:value-of select="."/>
        </FORMAT>
    </xsl:template>
</xsl:stylesheet>

Когда это преобразование применяется к предоставленному XML-документу (см. Выше), оно снова дает желаемый, правильный результат.

Другие вопросы по тегам