【问题标题】:XSL 2.0 for-each-group group-ending-with scope of position()XSL 2.0 for-each-group group-ending-with scope of position()
【发布时间】:2018-03-29 10:59:17
【问题描述】:

我想使用 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

这里提到,模式表达式与选择表达式相比,它们对范围的解释有所不同。看完后,不知道如何改变我对模式表达式的使用来实现我期望的行为。

Using for-each-group for high performance XSLT

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

当前行为不是我正在寻找的行为。

【问题讨论】:

    标签: xslt xslt-2.0 saxon


    【解决方案1】:

    Tim C 已经解释了如何获得所需的行为;这只是帮助您理解错误的说明。

    position() 函数和动态上下文

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

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

    特别是,XPath specification 规定“当表达式 E1/E2E1[E2] 被计算时,通过计算 E1 获得的序列中的每个项目都成为内部焦点中的上下文项目,用于计算 @ 987654329@。”

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

    在表达式/market/department/product[position() mod 4 = 0]中,刚才引用的规则表示表达式product[position() mod 4 = 0]/market/department'. That is, for eachdepartmentelement in that sequence, the expressionproduct[...]is evaluated. That right-hand expression in turn is equivalent tochild::product序列中的每个项目分别进行评估[...], so for each evaluation of the right-hand expression the sequence in question is the sequence of elements namedproductwhich are children of the currentdepartmentelement. Within the expressionproduct[position() mod 4 = 0], the same basic rule applies: the filter expression within square brackets is evaluated in the context given by the expressionproduct. As a consequence, the context position (the value returned byposition()) is the position of the currentproductelement among its sibling elements. Since nodepartmentelement in the input has as many as four children, the value ofposition() ` 永远不会大于 3,并且每个过滤器表达式的计算结果为 false,因此整个表达式的计算结果为空序列。

    具有不同值的相似表达式

    相比之下,在表达式(/market/department/product)[position() mod 4 = 0] 中,过滤器表达式是在文档中所有product 元素的序列的上下文中计算的(严格来说,具有指定路径的那些,在这种情况下是所有文档中的产品元素)。作为不同部门元素的子元素的产品元素被集中到相同的序列中,并且然后对每个元素应用一次谓词。 position() 的取值范围为 1 到 12,整体表达式选择值为胡萝卜、餐巾纸和袜子的产品。

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

    但是你应该清楚position()总是并且只表示一个节点在其父节点的子节点中的位置。

    一个简单的算术示例

    考虑一些完全不涉及节点的表达式可能会有所帮助。

    表达式

    (1 to 100)
    

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

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

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

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

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

    【讨论】:

      【解决方案2】:

      在这里尝试使用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 的整数对项目进行分组。

      【讨论】:

        猜你喜欢
        • 2016-04-09
        • 1970-01-01
        • 2021-03-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多