2009-11-01 1 views
3

Я не понимаю, выход из этой таблицы стилей:Действительно ли xsl: последовательность всегда не пуста?

<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 в this template и проверить ее на пустоту позже).

ответ

5

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

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

<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), но действительно ли это полезно или делает ваш код более понятным.

+0

Большое спасибо! Для справки, вот ссылка на соответствующую часть документации (если я правильно понимаю): http://www.w3.org/TR/xslt20/#temporary-trees –

+1

Да, вы понимаете это правильно. Эта часть спецификации показывает, что вы неявно имеете инструкцию 'xsl: document'. Он также указывает на то, о чем я не упоминал: вы можете использовать атрибут 'as', если вам нужен определенный тип вывода. Последовательность внутри 'xsl: variable' должна быть конвертируемой в этот тип или произошла ошибка. – Abel

2

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

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

даст вам выход вы находитесь ожидая.

+0

благодарственное вы за ответ. Я не уверен, что понимаю, что ... что именно хранится в моей переменной seq? Then (например, когда обрабатывается из ввода samle). Я думал, что это последовательность из двух '' 'элементов. –

3

Это работает, как можно было бы ожидать, если вы либо изменить переменную декларацию:

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

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

<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 
+0

Спасибо за подсказки 'item() *' и 'explain' (да, я использую саксон). –

Смежные вопросы