基于匹配元素的 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>