Является ли xsl: sequence всегда непустым? - PullRequest
3 голосов
/ 01 ноября 2009

Я не понимаю вывод этой таблицы стилей:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
        <xsl:apply-templates select="root/sub"/>
    </xsl:template>

    <xsl:template match="sub">
        <xsl:variable name="seq">
            <xsl:sequence select="*" />
        </xsl:variable>

        <xsl:message>
            <xsl:value-of select="@id" />
            <xsl:text>: </xsl:text>
            <xsl:value-of select="count($seq)" />
        </xsl:message>
    </xsl:template>
</xsl:stylesheet>

применительно к следующему XML:

<root>
    <sub id="empty" />
    <sub id="one"><one/></sub>
    <sub id="two"><one/><one/></sub>
    <sub id="three"><one/><one/><one/></sub>
</root>

Вывод, записанный элементом xsl:message:

empty: 1
one: 1
two: 1
three: 1

Вместо этого я ожидал:

empty: 0
one: 1
two: 2
three: 3

Почему count($seq) всегда возвращает 1 в этом случае? Как бы вы изменили определение переменной, чтобы я потом смог проверить ее на пустоту? (Простой <xsl:variable name='seq' select='*' /> вернул бы ожидаемый ответ, но это не вариант ... Я хочу изменить between переменную в этом шаблоне и проверить его на пустоту позже.

Ответы [ 3 ]

5 голосов
/ 02 ноября 2009

Позвольте мне ответить на вопрос «почему» на ваш вопрос.

Если вы напишите следующее утверждение:

<xsl:variable name="x" select="*" />

переменная $x содержит последовательность дочерних узлов текущего узла. В $x нет неявного родительского узла, потому что вы используете select. Теперь рассмотрим следующее:

<xsl:variable name="x">
    <content />
    <content />
</xsl:variable>

где переменная $x содержит последовательность одного узла: родительский узел content. Здесь count($x) всегда даст вам 1. Чтобы получить количество элементов content, необходимо подсчитать дочерние элементы неявного корневого узла $x: count($x/content).

Как правило, вы можете помнить, что: , если xsl:variable сам является пустым элементом с атрибутом select, результирующий набор будет присвоен переменной. Если вы используете xsl:variable без атрибута select, вы всегда создаете неявного родителя с переменной, а содержимое переменной - это дочерние элементы под ним .

То же самое относится к вашему примеру с xsl:sequence как ребенок к xsl:variable. Имея непустой xsl:variable, вы создаете неявного родителя для последовательности, поэтому вы всегда получаете 1, если подсчитываете саму переменную.

Если вам нужны оба: набор операторов внутри xsl:variable, но поведение атрибута select, вы можете обойти это, используя следующее:

<xsl:variable name="x" select="$y/*" />

<xsl:variable name="y">
    <xsl:sequence select="foo" />
</xsl:variable>

, который теперь даст ожидаемую сумму для count($x), но можно ли утверждать, что это действительно выгодно или делает ваш код более понятным.

3 голосов
/ 02 ноября 2009

Это работает, как и следовало ожидать, если вы измените объявление переменной на:

<xsl:variable name="seq" select="*"/>

или объявите тип переменной, используя атрибут 'as':

<xsl:variable name="seq" as="item()*">
        <xsl:sequence select="*" />
</xsl:variable>

Отсутствие информации о типах часто приводит к неожиданным результатам в XSLT 2.0. Если вы используете Saxon, вы можете вывести, как Saxon интерпретирует таблицу стилей, используя атрибут расширения объяснения:

    <xsl:template match="sub" saxon:explain="yes" xmlns:saxon="http://saxon.sf.net/">
    <xsl:variable name="seq">
        <xsl:sequence select="*" />
    </xsl:variable>

    <xsl:message>
        <xsl:value-of select="@id" />
        <xsl:text>: </xsl:text>
        <xsl:value-of select="count($seq)" />
    </xsl:message>
</xsl:template>

Как видите, Saxon создает узел документа из последовательности:

Optimized expression tree for template at line 6 in :
                    let $seq[refCount=1] as document-node() :=
                      document-constructor
                        child::element()
                    return
                      message
2 голосов
/ 02 ноября 2009

Вы выбираете подчиненные узлы, а затем подсчитываете каждый узел - так что это всегда будет 1. Вам нужно сосчитать детей, например:

<xsl:value-of select="count($seq/*)" />

даст вам ожидаемый результат.

...