基于匹配元素的 XSLT 2.0 Group/Merging 个节点
XSLT 2.0 Group/Merging nodes based on matching elements
我是 XSLT 的新手,一直在尝试合并与键(元素)匹配的节点。我尝试了其他一些解决方案,但在我的数据集上没有正确理解它。
输入:
<coll>
<rootNode>
<Header>
<code> 1234 </code> <-- key to match on
<name> Name1 </name>
</Header>
<node2> Any text </node2>
<node4> Any data here </node4>
<children>
<childID> 3456 </childID>
<type> Child </type>
</children>
</rootNode>
<rootNode>
<Header>
<code> 1234 </code>
<name> Name1 </name>
</Header>
<node2> Different Text </node2>
<node4> Different data here </node4>
<children>
<childID> 789 </childID>
<type> Parent </type>
</children>
</rootNode>
</coll>
预期输出:
<coll>
<rootNode>
<Header>
<code> 1234 </code>
<name> Name1 </name>
</Header>
<node2> Any text </node2>
<node4> Any data here </node4>
<node2> Different Text </node2>
<node4> Different data here </node4>
<children>
<childID> 3456 </childID>
<type> Child </type>
<childID> 789 </childID>
<type> Parent </type>
</children>
</rootNode>
</coll>
即匹配 Header/code 值,然后在 any 子节点具有不同值的地方合并。因此任何具有相同值的节点都不会重复。
希望这是有道理的,我的第一个 SO post,谢谢!
这是一个例子:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:functx="http://www.functx.com"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="xs functx">
<xsl:output indent="yes"/>
<xsl:function name="functx:index-of-node" as="xs:integer*">
<xsl:param name="nodes" as="node()*"/>
<xsl:param name="nodeToFind" as="node()"/>
<xsl:sequence select="
for $seq in (1 to count($nodes))
return $seq[$nodes[$seq] is $nodeToFind]
"/>
</xsl:function>
<xsl:function name="mf:eliminate-deep-equal-duplicates" as="node()*">
<xsl:param name="nodes"/>
<xsl:sequence
select="for $node in $nodes
return $node[not(some $preceding-node in $nodes[position() lt functx:index-of-node($nodes, $node)] satisfies deep-equal($node, $preceding-node))]"/>
</xsl:function>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* , node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="coll">
<xsl:copy>
<xsl:for-each-group select="rootNode" group-by="Header/code">
<xsl:copy>
<xsl:apply-templates select="Header,
mf:eliminate-deep-equal-duplicates(current-group()/(* except (Header, children))),
children"/>
</xsl:copy>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
<xsl:template match="rootNode/children">
<xsl:copy>
<xsl:apply-templates select="mf:eliminate-deep-equal-duplicates(current-group()/children/*)"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
至于解释:发布的样式表有三个模板,第一个是身份转换模板(允许我们通过在其他模板中对它们执行 apply-templates
来复制我们想要复制的元素),第二个匹配的 coll
元素创建它们的浅表副本,然后在 rootNode
元素上应用教科书 for-each-group
,按照您的要求按 Header/code
对它们进行分组。在 for-each-group
中,对于每个组,xsl:copy
创建一个 rootNode
并通过处理组中第一项的 Header
元素来填充它(因此我们只得到一个 Header
result element in each group), children except Header
and children
of each item in the group where has no previous deep-equals
element in the group and the children
组中第一项的元素,以确保每个组都获得一个 children
子元素。在该元素的模板中,我们需要确保我们处理当前组中所有在他们之前没有 deep-equal
重复项的孙子。
我已将长表达式重构为一个函数 mf:eliminate-deep-equal-duplicates
,它会选择节点序列中的那些节点,其中相同序列中的前一个节点不为 deep-equal
。
该解决方案利用了 functx 库的函数 http://www.xsltfunctions.com/xsl/functx_index-of-node.html,它为我们提供了序列中节点的索引。
正如 Vladimir Nesterovsky 在评论中指出的那样,函数 mf:eliminate-deep-equal-duplicates
也可以在不使用 functx functx:index-of-node
:
的情况下实现
<xsl:function name="mf:eliminate-deep-equal-duplicates" as="node()*">
<xsl:param name="nodes"/>
<xsl:sequence
select="for $i in (1 to count($nodes)),
$node in $nodes[$i]
return $node[not(some $preceding-node in $nodes[position() lt $i] satisfies deep-equal($node, $preceding-node))]"/>
</xsl:function>
我是 XSLT 的新手,一直在尝试合并与键(元素)匹配的节点。我尝试了其他一些解决方案,但在我的数据集上没有正确理解它。
输入:
<coll>
<rootNode>
<Header>
<code> 1234 </code> <-- key to match on
<name> Name1 </name>
</Header>
<node2> Any text </node2>
<node4> Any data here </node4>
<children>
<childID> 3456 </childID>
<type> Child </type>
</children>
</rootNode>
<rootNode>
<Header>
<code> 1234 </code>
<name> Name1 </name>
</Header>
<node2> Different Text </node2>
<node4> Different data here </node4>
<children>
<childID> 789 </childID>
<type> Parent </type>
</children>
</rootNode>
</coll>
预期输出:
<coll>
<rootNode>
<Header>
<code> 1234 </code>
<name> Name1 </name>
</Header>
<node2> Any text </node2>
<node4> Any data here </node4>
<node2> Different Text </node2>
<node4> Different data here </node4>
<children>
<childID> 3456 </childID>
<type> Child </type>
<childID> 789 </childID>
<type> Parent </type>
</children>
</rootNode>
</coll>
即匹配 Header/code 值,然后在 any 子节点具有不同值的地方合并。因此任何具有相同值的节点都不会重复。
希望这是有道理的,我的第一个 SO post,谢谢!
这是一个例子:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:functx="http://www.functx.com"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="xs functx">
<xsl:output indent="yes"/>
<xsl:function name="functx:index-of-node" as="xs:integer*">
<xsl:param name="nodes" as="node()*"/>
<xsl:param name="nodeToFind" as="node()"/>
<xsl:sequence select="
for $seq in (1 to count($nodes))
return $seq[$nodes[$seq] is $nodeToFind]
"/>
</xsl:function>
<xsl:function name="mf:eliminate-deep-equal-duplicates" as="node()*">
<xsl:param name="nodes"/>
<xsl:sequence
select="for $node in $nodes
return $node[not(some $preceding-node in $nodes[position() lt functx:index-of-node($nodes, $node)] satisfies deep-equal($node, $preceding-node))]"/>
</xsl:function>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* , node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="coll">
<xsl:copy>
<xsl:for-each-group select="rootNode" group-by="Header/code">
<xsl:copy>
<xsl:apply-templates select="Header,
mf:eliminate-deep-equal-duplicates(current-group()/(* except (Header, children))),
children"/>
</xsl:copy>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
<xsl:template match="rootNode/children">
<xsl:copy>
<xsl:apply-templates select="mf:eliminate-deep-equal-duplicates(current-group()/children/*)"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
至于解释:发布的样式表有三个模板,第一个是身份转换模板(允许我们通过在其他模板中对它们执行 apply-templates
来复制我们想要复制的元素),第二个匹配的 coll
元素创建它们的浅表副本,然后在 rootNode
元素上应用教科书 for-each-group
,按照您的要求按 Header/code
对它们进行分组。在 for-each-group
中,对于每个组,xsl:copy
创建一个 rootNode
并通过处理组中第一项的 Header
元素来填充它(因此我们只得到一个 Header
result element in each group), children except Header
and children
of each item in the group where has no previous deep-equals
element in the group and the children
组中第一项的元素,以确保每个组都获得一个 children
子元素。在该元素的模板中,我们需要确保我们处理当前组中所有在他们之前没有 deep-equal
重复项的孙子。
我已将长表达式重构为一个函数 mf:eliminate-deep-equal-duplicates
,它会选择节点序列中的那些节点,其中相同序列中的前一个节点不为 deep-equal
。
该解决方案利用了 functx 库的函数 http://www.xsltfunctions.com/xsl/functx_index-of-node.html,它为我们提供了序列中节点的索引。
正如 Vladimir Nesterovsky 在评论中指出的那样,函数 mf:eliminate-deep-equal-duplicates
也可以在不使用 functx functx:index-of-node
:
<xsl:function name="mf:eliminate-deep-equal-duplicates" as="node()*">
<xsl:param name="nodes"/>
<xsl:sequence
select="for $i in (1 to count($nodes)),
$node in $nodes[$i]
return $node[not(some $preceding-node in $nodes[position() lt $i] satisfies deep-equal($node, $preceding-node))]"/>
</xsl:function>