Является ли xsl:sequence всегда непустым?
Я не понимаю вывод из этой таблицы стилей:
<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 ответа
Позвольте мне попытаться ответить на вопрос "почему" на ваш вопрос.
Если вы напишите следующее заявление:
<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)
Но спорным является ли это действительно полезным или делает ваш код более понятным.
Это работает так, как вы можете ожидать, если вы либо измените объявление переменной на:
<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
Вы выбираете подчиненные узлы, а затем подсчитываете каждый узел - так что это всегда будет 1. Вам нужно сосчитать детей, например:
<xsl:value-of select="count($seq/*)" />
даст вам ожидаемый результат.