XSL 2.0 for-each-group group-ending-with scope of position()
XSL 2.0 for-each-group group-ending-with scope of position()
我想使用 XSL 2.0 (saxon9he.jar) 将数据按位置分组。
在此示例中,我尝试将市场产品分成袋子,每个袋子中有 4 件物品。
我的测试表明 position() 在父级的范围内。这样土豆作为蔬菜部门的子项位于第 2 位,而不是我的 selection 产品中的第 5 位。
我想将组基于 selection 中的位置,而不是父级中的位置。
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 个项目。
每个包都在一个部门的第 2 个项目结束,除了第一个包之外的所有包都包含上一个部门的最后一个项目。
结果bag1.txt
1applebanana
结果bag1.txt
2grapecarrotpotato
预期 bag1.txt
1applebanana
预期 bag2.txt
2grapecarrot
这向我表明 position() 与父项相关,而不是 selection。
我希望 position() 与 selection 相关。
根据我的研究,position() 应该与 selection 有关。
就像这里的答案中描述的那样:
Final hint: position() does not tell you the position of the node
within its parent. It tells you the position of the current node
relative to the list of nodes you are processing right now.
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 个纸制品。
当前行为不是我要查找的行为。
尝试在这里使用 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 的整数对项目进行分组。
Tim C 已经解释了如何获得所需的行为;这只是帮助您理解错误的注释。
position() 函数和动态上下文
position()
函数 return 确定项目在给定序列中的位置,其标识由上下文给出。该函数通常会 return 确定元素在其父元素的子元素中的位置,但这是因为在实践中,为 XPath 表达式求值确定动态上下文的规则通常指定相关序列是元素的子节点。 position()
函数是 而不是 'scoped' 作为其定义的一部分的父元素。
position()
函数的值是上下文位置,定义为"the position of the context item within the sequence of items currently being processed"。与上下文项一样,上下文位置(以及由 last()
编辑的上下文大小 return)是动态上下文的一部分,在其中计算 XPath 表达式。在对任何非原子 XPath 表达式求值时,不同子表达式的动态上下文可能不同。
特别是 XPath specification 规定 "When an expression E1/E2
or E1[E2]
is evaluated, each item in the sequence obtained by evaluating E1
becomes the context item in the inner focus for an evaluation of E2
."
group-ending-with 属性中的表达式
在表达式 /market/department/product[position() mod 4 = 0]
中,刚才引用的规则意味着表达式 product[position() mod 4 = 0]
对序列 /market/department'. That is, for each
departmentelement 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
productwhich are children of the current
departmentelement. 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
productelement among its sibling elements. Since no
departmentelement in the input has as many as four children, the value of
position()` 永远不会大于 3,并且每个过滤器表达式的计算结果为 false,因此表达式为整体评估为空序列。
具有不同值的相似表达式
在表达式 (/market/department/product)[position() mod 4 = 0]
中,相比之下,过滤器表达式是在文档中所有 product
元素的序列的上下文中计算的(严格来说,那些具有指定路径的,在这种情况下是文档中的所有产品元素)。作为不同部门元素的子元素的产品元素被集中到相同的序列中,并且 then 谓词对每个元素应用一次。 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]
returns由序列S2中的第24和25个条目组成的序列,即(96, 100).
我想使用 XSL 2.0 (saxon9he.jar) 将数据按位置分组。 在此示例中,我尝试将市场产品分成袋子,每个袋子中有 4 件物品。 我的测试表明 position() 在父级的范围内。这样土豆作为蔬菜部门的子项位于第 2 位,而不是我的 selection 产品中的第 5 位。 我想将组基于 selection 中的位置,而不是父级中的位置。
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 个项目。
每个包都在一个部门的第 2 个项目结束,除了第一个包之外的所有包都包含上一个部门的最后一个项目。
结果bag1.txt
1applebanana
结果bag1.txt
2grapecarrotpotato
预期 bag1.txt
1applebanana
预期 bag2.txt
2grapecarrot
这向我表明 position() 与父项相关,而不是 selection。 我希望 position() 与 selection 相关。 根据我的研究,position() 应该与 selection 有关。 就像这里的答案中描述的那样:
Final hint: position() does not tell you the position of the node within its parent. It tells you the position of the current node relative to the list of nodes you are processing right now.
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 个纸制品。
当前行为不是我要查找的行为。
尝试在这里使用 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 的整数对项目进行分组。
Tim C 已经解释了如何获得所需的行为;这只是帮助您理解错误的注释。
position() 函数和动态上下文
position()
函数 return 确定项目在给定序列中的位置,其标识由上下文给出。该函数通常会 return 确定元素在其父元素的子元素中的位置,但这是因为在实践中,为 XPath 表达式求值确定动态上下文的规则通常指定相关序列是元素的子节点。 position()
函数是 而不是 'scoped' 作为其定义的一部分的父元素。
position()
函数的值是上下文位置,定义为"the position of the context item within the sequence of items currently being processed"。与上下文项一样,上下文位置(以及由 last()
编辑的上下文大小 return)是动态上下文的一部分,在其中计算 XPath 表达式。在对任何非原子 XPath 表达式求值时,不同子表达式的动态上下文可能不同。
特别是 XPath specification 规定 "When an expression E1/E2
or E1[E2]
is evaluated, each item in the sequence obtained by evaluating E1
becomes the context item in the inner focus for an evaluation of E2
."
group-ending-with 属性中的表达式
在表达式 /market/department/product[position() mod 4 = 0]
中,刚才引用的规则意味着表达式 product[position() mod 4 = 0]
对序列 /market/department'. That is, for each
departmentelement 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
productwhich are children of the current
departmentelement. 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
productelement among its sibling elements. Since no
departmentelement in the input has as many as four children, the value of
position()` 永远不会大于 3,并且每个过滤器表达式的计算结果为 false,因此表达式为整体评估为空序列。
具有不同值的相似表达式
在表达式 (/market/department/product)[position() mod 4 = 0]
中,相比之下,过滤器表达式是在文档中所有 product
元素的序列的上下文中计算的(严格来说,那些具有指定路径的,在这种情况下是文档中的所有产品元素)。作为不同部门元素的子元素的产品元素被集中到相同的序列中,并且 then 谓词对每个元素应用一次。 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]
returns由序列S2中的第24和25个条目组成的序列,即(96, 100).