XSLT 2.0基于匹配元素的组/合并节点

时间:2016-01-19 17:52:21

标签: xml xslt merge xslt-2.0

我是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>

即。匹配标头/代码值,然后合并任何子节点具有不同值的位置。因此,具有相同值的任何节点都不会重复。

希望这是有道理的,我的第一篇SO帖子,谢谢!

1 个答案:

答案 0 :(得分:2)

以下是一个例子:

<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元素以创建它们的浅表副本,然后在for-each-group元素上应用教科书rootNode,按照Header/code对其进行分组,如您所要求的那样。在for-each-group内,对于每个组,xsl:copy创建rootNode并通过处理组中第一项的Header元素来填充它(因此我们只获得一个Header {1}}每个组中的结果元素),组中每个项目的Headerchildren除外,其中组中没有先前的deep-equals元素且{{1}组中第一个项目的元素,以确保每个组都获得children子元素。在该元素的模板中,我们需要确保我们处理当前组中没有children重复的所有大孩子。

我已将长表达式重构为函数deep-equal,它选择节点序列中的那些节点,同一序列中的前一节点不是mf:eliminate-deep-equal-duplicates

该解决方案利用了functx库的函数http://www.xsltfunctions.com/xsl/functx_index-of-node.html,该函数为我们提供了序列中节点的索引。

正如Vladimir Nesterovsky在评论中指出的那样,函数deep-equal也可以在不使用functx mf:eliminate-deep-equal-duplicates的情况下实现:

functx:index-of-node