代码之家  ›  专栏  ›  技术社区  ›  Munaaf Ghumran

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

  •  1
  • Munaaf Ghumran  · 技术社区  · 9 年前

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

    一、 e.匹配标题/代码值,然后合并 任何 子节点具有不同的值。因此,具有相同值的任何节点都不会重复。

    希望这有意义,我的第一个SO帖子,谢谢!

    1 回复  |  直到 9 年前
        1
  •  2
  •   Martin Honnen    8 年前

    下面是一个示例:

    <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 ,正如你所要求的。在 对于每组 ,对于每组 xsl:copy 创建一个 根节点 并通过处理 Header 元素(所以我们只得到一个 收割台 每组中的result元素),除 收割台 children 组中没有前一个项目的每个项目 deep-equals 组中的元素和 儿童 元素,以确保每个组都获得 儿童 子元素。在该元素的模板中,我们需要确保处理当前组中没有 deep-equal 在它们之前复制。

    我已将长表达式重构为函数 mf:eliminate-deep-equal-duplicates ,它选择节点序列中没有相同序列中的前一个节点的那些节点 深度相等 .

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

    正如弗拉基米尔·内斯特罗夫斯基在评论中指出的 mf:消除深度相等重复 也可以在不使用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>