首页 文章

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

提问于
浏览
0

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

Expected bag1.txt

1applebananagrapecarrot

Expected bag2.txt

2potatosquashplatesnapkins

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

结果是bag1.txt

1applebanana

结果是bag1.txt

2grapecarrotpotato

Expected bag1.txt

1applebanana

Expected 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 回答

  • 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

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

    The position() function and the dynamic context

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

    position() 函数的值是上下文位置,定义为"the position of the context item within the sequence of items currently being processed" . 与上下文项一样,上下文位置(以及 last() 返回的上下文大小)是评估XPath表达式的动态上下文的一部分 . 在评估任何非原子XPath表达式时,动态上下文对于不同的子表达式可能是不同的 .

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

    The expression in your group-ending-with attribute

    在表达式 /market/department/product[position() mod 4 = 0] 中,刚引用的规则意味着表达式 product[position() mod 4 = 0] 将针对序列中的每个项目单独计算 /market/department'. That is, for each department element in that sequence, the expression product [...] is evaluated. That right-hand expression in turn is equivalent to child :: product [...] , so for each evaluation of the right-hand expression the sequence in question is the sequence of elements named product which are children of the current department element. Within the expression product [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 . As a consequence, the context position (the value returned by position() ) is the position of the current product element among its sibling elements. Since no department element in the input has as many as four children, the value of position()`永远不会大于3,并且每个过滤器表达式的计算结果为false,因此表达式作为一个整体求值为空序列 .

    A similar expression with a different value

    相反,在表达式 (/market/department/product)[position() mod 4 = 0] 中,过滤器表达式在文档中所有 product 元素序列的上下文中进行评估(严格地说,那些具有指定路径的元素,在这种情况下是所有产品元素)文献) . 作为不同部门元素的子元素的产品元素被集中到相同的序列中,然后对每个元素应用谓词一次 . position() 的值范围为1到12,整个表达式选择值为carrot,napkins和socks的产品 .

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

    但是你应该清楚自己的想法是 position() 始终只表示一个节点在其父节点之间的位置 .

    A simple arithmetic example

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

    表达方式

    (1 to 100)
    

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

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

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

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

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

相关问题