对排序顺序的操作不遵守排序顺序

时间:2013-12-06 23:27:07

标签: xml sorting xslt saxon

我无法理解为什么XSLT(Saxon 9.1和9.5)在使用包含排序序列的变量时的工作方式。

这是我的程序的输出,其中嵌入了我的三个问题:

<?xml version="1.0" encoding="UTF-8"?>
The following sequence is unsorted...

  sequence $list                        = (<contribution e="4"/><contribution e="1"/><contribution e="2"/><contribution e="8"/>)

It is, as I expected, in document order.

The following sequence is explicitly sorted...

  sequence $sorted-list                 = (<contribution e="8"/><contribution e="4"/><contribution e="2"/><contribution e="1"/>)

It is, as I expected, sorted in descending numerical @e order.

In the following output, I expect for the @e values from $list to be in document order,
and for the @e values from $sorted-list to be in descending numerical order...

  value-of $list/@e                     = (4 1 2 8)
  value-of $sorted-list/@e              = (4 1 2 8)

But both are in document order.

Specifically, the $sorted-list/@e values are NOT listed in descending numerical @e order.
(Question 1: ...By the way, why can't I use 'xsl:sequence select="$list/@e"' here?)

Next, here's the real work that I'm interested in. It is a function that computes a running
subtotal of the elements passed in. Order is critical here. I expect for my sorted list to provide
@e values to the function in the order that I explicitly put them in the definition of $sorted-list.
So, in the following call, I send in the sequence that I think SHOULD have been sorted...

  sequence f($sorted-list/@e)           = (0 4 5 7 15)

But my result set is a list of running subtotals that are obviously not correctly ordered.
Question 2: Is this a bug, or evidence of a gap in my comprehension?

In the following call, I send a sequence that's explicitly ordered...

  sequence f(for...$sorted-list[$i]/@e) = (0 8 12 14 15)

...and I receive the sequence I need. I can use this as a workaround.

Question 3: Why is it that I must explicitly [re-]sort the $sorted-list sequence as I pass it to util:f()?

这是我的输入XML:

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <contribution e="4"/>
    <contribution e="1"/>
    <contribution e="2"/>
    <contribution e="8"/>
</root>

......还有我的XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:util="http://www.method-r.com/util"
    exclude-result-prefixes="xs util"
    version="2.0">

    <xsl:template match="/">
The following sequence is unsorted...

  sequence $list                        = (<xsl:sequence select="$list"/>)

It is, as I expected, in document order.

The following sequence is explicitly sorted...

  sequence $sorted-list                 = (<xsl:sequence select="$sorted-list"/>)

It is, as I expected, sorted in descending numerical @e order.

In the following output, I expect for the @e values from $list to be in document order,
and for the @e values from $sorted-list to be in descending numerical order...

  value-of $list/@e                     = (<xsl:value-of select="$list/@e"/>)
  value-of $sorted-list/@e              = (<xsl:value-of select="$sorted-list/@e"/>)

But both are in document order.

Specifically, the $sorted-list/@e values are NOT listed in descending numerical @e order.
(Question 1: ...By the way, why can't I use 'xsl:sequence select="$list/@e"' here?)

Next, here's the real work that I'm interested in. It is a function that computes a running
subtotal of the elements passed in. Order is critical here. I expect for my sorted list to provide
@e values to the function in the order that I explicitly put them in the definition of $sorted-list.
So, in the following call, I send in the sequence that I think SHOULD have been sorted...

  sequence f($sorted-list/@e)           = (<xsl:sequence select="util:f(0, $sorted-list/@e)"/>)

But my result set is a list of running subtotals that are obviously not correctly ordered.
Question 2: Is this a bug, or evidence of a gap in my comprehension?

In the following call, I send a sequence that's explicitly ordered...

  sequence f(for...$sorted-list[$i]/@e) = (<xsl:sequence select="util:f(0, for $i in 1 to count($sorted-list) return $sorted-list[$i]/@e)"/>)

...and I receive the sequence I need. I can use this as a workaround.

Question 3: Why is it that I must explicitly [re-]sort the $sorted-list sequence as I pass it to util:f()?

<xsl:text/>
    </xsl:template>



    <xsl:variable name="list" as="element()*" select="//contribution"/>

    <xsl:variable name="sorted-list" as="element()*">
        <xsl:perform-sort select="$list">
            <xsl:sort select="number(@e)" order="descending"/>
        </xsl:perform-sort>
    </xsl:variable>



    <xsl:function name="util:f" as="xs:double*">
        <xsl:param name="sum0" as="xs:double"/>
        <xsl:param name="list" as="xs:double*"/>
        <xsl:sequence select="util:cumulative-list-2((), $list)"/>
    </xsl:function>

    <xsl:function name="util:cumulative-list-2" as="xs:double*">
        <xsl:param name="list" as="xs:double*"/>
        <xsl:param name="list-remainder" as="xs:double*"/>
        <xsl:choose>
            <xsl:when test="not(exists($list-remainder))">
                <xsl:sequence select="$list"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:sequence select="util:cumulative-list-2(
                    if (empty($list)) then
                    (0, $list-remainder[1])
                    else 
                    ($list, $list[last()] + $list-remainder[1]),
                    remove($list-remainder, 1)
                    )"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:function> 


</xsl:stylesheet>

1 个答案:

答案 0 :(得分:2)

通过执行$sorted-list/@e的步骤,/@e将选择所有@e属性并按文档顺序排序,因此如果您有已排序的序列并想要输出@e属性按顺序排列,您无法使用$sorted-list/@e,而是必须使用for $item in $sorted-list return $item/@e

请参阅http://www.w3.org/TR/xpath20/#id-path-expressions说明:

  

每个操作E1 / E2的评估如下:......如果每次评估   E2返回一个(可能是空的)节点序列,这些序列   组合,并根据节点消除重复节点   身份。生成的节点序列按文档顺序返回。

如果您使用Saxon 9.5的商业版本,那么您还应该能够使用http://www.w3.org/TR/xpath-30/#id-map-operator$sorted-list ! @e来保留订单。