代码之家  ›  专栏  ›  技术社区  ›  Peter Å tibraný

xsl:sequence是否总是非空的?

  •  3
  • Peter Å tibraný  · 技术社区  · 16 年前

    我不理解此样式表的输出:

    <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 ,稍后测试它是否空虚)。

    3 回复  |  直到 16 年前
        1
  •  5
  •   Abel    16 年前

    让我试着回答你问题的“为什么”部分。

    如果你写下以下陈述:

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

    变量 $x 包含当前节点的子节点的序列。中没有隐式父节点 X ,因为您使用 select . 现在考虑以下内容:

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

    可变位置 X 包含一个节点的序列:的父节点 content . 在这里, count($x) 会一直给你 1 . 得到 内容 元素,您需要计算 X 隐式根节点: count($x/content) .

    根据经验,您可以记住: 如果 xsl:variable 它本身是一个带有 选择 属性,结果集将分配给变量。如果你使用 变量: 没有 选择 属性,您总是用变量创建一个隐式父级,变量的内容是它下面的子级 .

    同样适用于您的示例 xsl:sequence 小时候 变量: . 有一个非空的 变量: 为序列创建隐式父级,这就是为什么您总是 如果对变量本身进行计数。

    如果两者都需要:内部的一组语句 变量: 但是 选择 属性,您可以使用以下方法解决此问题:

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

    这将产生预期的金额 伯爵(X) 但是,无论这是否真的有益,还是让代码更清晰,都是有争议的。

        2
  •  3
  •   xixxix    16 年前

    如果将变量声明更改为:

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

    或使用“as”属性声明变量的类型:

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

    不指定任何类型信息通常会导致在XSLT2.0中出现令人惊讶的结果。如果使用的是Saxon,则可以使用explain扩展属性输出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
    
        3
  •  2
  •   FinnNk    16 年前

    您要选择子节点,然后对每个节点进行计数-因此它始终为1。您需要对孩子进行计数,例如:

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

    将提供您期望的输出。