【问题标题】:Is xsl:sequence always non-empty?xsl:sequence 总是非空的吗?
【发布时间】:2009-11-01 17:05:00
【问题描述】:

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

<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?您将如何更改变量定义,以便我以后可以测试它是否为空? (简单的&lt;xsl:variable name='seq' select='*' /&gt; 会返回预期的答案,但不是一个选项......我想更改this template 中的between 变量,并稍后测试它是否为空)。

【问题讨论】:

    标签: xslt xpath xslt-2.0 xsl-variable


    【解决方案1】:

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

    如果你写如下语句:

    <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) 的预期数量,但是否真的有益或使您的代码更清晰是有争议的。

    【讨论】:

    • 非常感谢!作为参考,这里是文档相关部分的链接(如果我理解正确的话):w3.org/TR/xslt20/#temporary-trees
    • 是的,您确实理解正确。规范的那部分表明您隐含了 xsl:document 指令。它还指出了我没有提到的其他内容:如果您需要特定的输出类型,可以使用 as 属性。 xsl:variable里面的序列必须能转换成这种类型,否则会出错。
    【解决方案2】:

    如果您将变量声明更改为:

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

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

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

    不指定任何类型信息通常会在 XSLT 2.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
    

    【讨论】:

    • 感谢item()*explain 的提示(是的,我使用的是撒克逊语)。
    【解决方案3】:

    您正在选择子节点,然后计算每个节点 - 所以它总是 1。您需要计算子节点,例如:

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

    会给你你期望的输出。

    【讨论】:

    • 感谢您的回答。我不确定我是否理解......然后我的 seq 变量中究竟存储了什么(例如,当 正在从样本输入处理时)。我认为这是两个one 元素的序列。
    猜你喜欢
    • 2013-06-06
    • 1970-01-01
    • 2017-01-18
    • 2011-06-29
    • 1970-01-01
    • 2017-11-24
    • 2012-12-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多