我想使用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种纸制品。
目前的行为不是我要找的行为。
答案 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)
position()函数和动态上下文
position()
函数返回给定序列中项目的位置,该序列的标识由上下文给出。该函数通常会返回元素在其父元素之间的位置,但这是因为在实践中,确定用于评估XPath表达式的动态上下文的规则通常指定相关序列是元素子节点的序列。作为其定义的一部分,position()
函数不'作用域'到父元素。
position()
函数的值是上下文位置,其定义为“当前正在处理的项目序列中的上下文项的位置”。与上下文项一样,上下文位置(以及last()
返回的上下文大小)是评估XPath表达式的动态上下文的一部分。在评估任何非原子XPath表达式时,动态上下文对于不同的子表达式可能是不同的。
特别是,XPath specification规定“当评估表达式E1/E2
或E1[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)。