XSL 2.0 for-each-group group-ending-position scope of position()

时间:2017-10-17 17:01:22

标签: xslt xslt-2.0 saxon

我想使用XSL 2.0(saxon9he.jar)按位置将数据拆分成组。 在这个样本中,我尝试将市场产品分成每个袋子中有4个物品的袋子。 我的测试表明position()在父级的范围内。马铃薯在蔬菜部门的孩子中排名第2,而不是在我选择的产品中排名第5。 我想基于选择中的位置,而不是父母中的位置。

XML数据集:

<market>
    <department name="fruit">
        <product>apple</product>
        <product>banana</product>
        <product>grape</product>
    </department>
    <department name="vegetable">
        <product>carrot</product>
        <product>potato</product>
        <product>squash</product>
    </department>
    <department name="paper">
        <product>plates</product>
        <product>napkins</product>
        <product>cups</product>
    </department>
    <department name="cloths">
        <product>shirts</product>
        <product>shorts</product>
        <product>socks</product>
    </department>
</market>

XSL模板:

<xsl:transform version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" exclude-result-prefixes="xs fn">
    <xsl:output indent="no" method="text"/>

    <!-- place 4 items in each bag -->

    <xsl:template match="/">
        <xsl:for-each-group select="/market/department/product" 
             group-ending-with="/market/department/product[position() mod 4 = 0]">
            <xsl:variable name="file" 
                 select="concat('bags/bag',position(),'.txt')"/>
            <xsl:result-document href="{$file}">

                <xsl:value-of select="position()"/>
                <xsl:for-each select="current-group()">
                    <xsl:value-of select="."/>
                </xsl:for-each>

           </xsl:result-document>
        </xsl:for-each-group>
    </xsl:template>

</xsl:transform>

结果bag1.txt

1applebananagrapecarrotpotatosquashplatesnapkinscupsshirtsshortssocks

结果bag2.txt

file does not exist!

预期 bag1.txt

1applebananagrapecarrot

预期 bag2.txt

2potatosquashplatesnapkins

我的调试结论: 似乎position()永远不会是4(每个部门只有3个项目) 如果我将mod 4更改为mod 2我会收到多个行李,而行李1则包含2个项目。但除了最后一个之外的所有其他包含3个项目。 每个包都在一个部门的第二个项目结束,除了第一个包之外的所有包都包括前一个部门的最后一个项目。

结果bag1.txt

1applebanana

结果bag1.txt

2grapecarrotpotato

预期 bag1.txt

1applebanana

预期 bag2.txt

2grapecarrot

这告诉我,position()与父项相关,而不是与选择相关。 我希望position()与选择相关。 根据我的研究,position()应该与选择有关。 在这里的答案中描述了喜欢:

  

最终提示:position()不会告诉您节点的位置   在其父母内部。它告诉您当前节点的位置   相对于您正在处理的节点列表。

Find the position of an element within its parent with XSLT / XPath

这里提到模式表达式与select表达式相比,它们对范围的解释不同。阅读之后,我不知道如何改变我对模式表达式的使用来实现我期待的行为。

Using for-each-group for high performance XSLT

基于我目前观察到的行为: 如果我有9种水果,4种蔬菜和20种纸制品,并使用mod 5 bag1将包含前5种水果产品, bag2将包含最后4种水果+4种蔬菜+前5种纸制品。

目前的行为不是我要找的行为。

2 个答案:

答案 0 :(得分:1)

尝试在此使用group-adjacent,而不是group-ending-with

 <xsl:for-each-group select="/market/department/product" 
                     group-adjacent="floor((position() - 1) div 4)">

或者这......

 <xsl:for-each-group select="/market/department/product" 
                     group-adjacent="ceiling(position() div 4)">

因此,根据整数除以4的位置对项目进行分组。

答案 1 :(得分:1)

Tim C已经解释了如何获得理想的行为;这只是一个帮助您了解错误的注释。

position()函数和动态上下文

position()函数返回给定序列中项目的位置,该序列的标识由上下文给出。该函数通常会返回元素在其父元素之间的位置,但这是因为在实践中,确定用于评估XPath表达式的动态上下文的规则通常指定相关序列是元素子节点的序列。作为其定义的一部分,position()函数'作用域'到父元素。

position()函数的值是上下文位置,其定义为“当前正在处理的项目序列中的上下文项的位置”。与上下文项一样,上下文位置(以及last()返回的上下文大小)是评估XPath表达式的动态上下文的一部分。在评估任何非原子XPath表达式时,动态上下文对于不同的子表达式可能是不同的。

特别是,XPath specification规定“当评估表达式E1/E2E1[E2]时,通过评估E1获得的序列中的每个项目都成为上下文项目。评估E2的内心焦点。“

group-ending-with属性中的表达式

在表达式/market/department/product[position() mod 4 = 0]中,刚刚引用的规则意味着对product[position() mod 4 = 0]部门/market/department'. That is, for each产品序列中的每个项目单独评估表达式element in that sequence, the expression [... ] is evaluated. That right-hand expression in turn is equivalent to孩子::产品[...] , so for each evaluation of the right-hand expression the sequence in question is the sequence of elements named产品which are children of the current部门element. Within the expression产品[position()mod 4 = 0] , the same basic rule applies: the filter expression within square brackets is evaluated in the context given by the expression product {{ 1}} position(). As a consequence, the context position (the value returned by product ) is the position of the current department element among its sibling elements. Since no position()`永远不会大于3,每个过滤器表达式的计算结果为false,因此表达式作为一个整体求值为空序列

具有不同值的类似表达式

相反,在表达式element in the input has as many as four children, the value of中,过滤器表达式是在文档中所有(/market/department/product)[position() mod 4 = 0]元素的序列的上下文中进行评估的(严格来说,那些具有指定路径的元素,在此case是文档中的所有产品元素)。作为不同部门元素的子元素的产品元素被集中到相同的序列中,然后然后对每个元素应用谓词一次。 product的值范围为1到12,整个表达式选择值为carrot,napkins和socks的产品。

您不能简单地在position()属性中使用第二个表达式,因为它不被允许(属性值必须是模式,而不是一般的XPath表达式)。即使你可以,模板中还有其他问题需要修复。

但是你应该清楚自己的想法group-ending-with总是只表示一个节点在其父节点之间的位置。

一个简单的算术示例

考虑一些根本不涉及任何节点的表达式可能会有所帮助。

表达式

position()

表示从1到100的自然数序列,包括1和100。我会称之为S1。表达式

(1 to 100)
除了上下文位置可被4整除的那些之外,

从S1中过滤掉所有内容,因此它表示序列(4,8,...,96,100)。我会称之为S2。如果我们附加另一个过滤器表达式,则其上下文由序列S2给出,而不是由S1给出。所以

(1 to 100) [position() mod 4 eq 0]

返回序列S2中第24和第25个条目的序列,即(96,100)。