使用xslt基于xml中的多个属性删除重复节点

时间:2012-04-22 07:22:48

标签: xml xslt

我有这个输入XML,需要用xslt

进行转换
<root>
    <node id="a">
        <section id="a_1" method="run">
            <item id="0">
                <attribute>
                    <color>Red</color>
                </attribute>
            </item>
        </section>
        <section id="a_2">
            <item id="0">
                <attribute>
                    <color>Red</color>
                </attribute>
            </item>
        </section>
        <section id="a_1" method="run">
            <item id="0">
                <attribute>
                    <color>Red</color>
                </attribute>
            </item>
        </section>
    </node>
    <node id="b">
        <section id="b_1" method="create">
            <user id="b_1a">
                <attribute>
                    <name>John</name>
                </attribute>
            </user>
            <user id="b_1b">
                <attribute>a</attribute>
            </user>
        </section>
        <section id="b_1" method="create">
            <user id="b_1c">
                <attribute>a</attribute>
            </user>
        </section>
        <section id="b_2">
            <user id="b_1a">
                <attribute>
                    <name>John</name>
                </attribute>
            </user>
        </section>
    </node>
</root>

预期产出:

<root>
    <node id="a">
        <section id="a_1">
            <item id="0">
                <attribute>
                    <color>Red</color>
                </attribute>
            </item>
        </section>
        <section id="a_2">
            <item id="0">
                <attribute>
                    <color>Red</color>
                </attribute>
            </item>
        </section>
    </node>
    <node id="b">
        <section id="b_1" method="create">
            <user id="b_1a">
                <attribute>
                    <name>John</name>
                </attribute>
            </user>
            <user id="b_1b">
                <attribute>a</attribute>
            </user>
        </section>

        <section id="b_2">
            <user id="b_1a">
                <attribute>
                    <name>John</name>
                </attribute>
            </user>
        </section>
    </node>
</root>

哪个节点将被删除无关紧要,只要它具有相同的元素名称,id和方法,其中一个将被删除。 知道xsl是什么样的吗?

注意:元素名称可以是任何不必要的,并且在整个文件中有多个元素名称;只要它具有相同的元素名称,id和属性(例如方法=创建),其中一个将被删除。

非常感谢。 干杯, 约翰

2 个答案:

答案 0 :(得分:3)

<强>予。这是一个简短而有效的(使用密钥)XSLT 1.0转换:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:key name="kElemWithAttribs" match="*[@id and @method]"
  use="concat(name(), '+', @id, '+', @method)"/>

 <xsl:template match="node()|@*">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match=
  "*[@id and @method
    and
     not(generate-id()
        =
         generate-id(key('kElemWithAttribs',
                         concat(name(), '+', @id, '+', @method)
                         )[1]
                    )
         )
     ]"/>
</xsl:stylesheet>

在提供的XML文档上应用此转换时:

<root>
    <node id="a">
        <section id="a_1" method="run">
            <item id="0">
                <attribute>
                    <color>Red</color>
                </attribute>
            </item>
        </section>
        <section id="a_2">
            <item id="0">
                <attribute>
                    <color>Red</color>
                </attribute>
            </item>
        </section>
        <section id="a_1" method="run">
            <item id="0">
                <attribute>
                    <color>Red</color>
                </attribute>
            </item>
        </section>
    </node>
    <node id="b">
        <section id="b_1" method="create">
            <user id="b_1a">
                <attribute>
                    <name>John</name>
                </attribute>
            </user>
            <user id="b_1b">
                <attribute>a</attribute>
            </user>
        </section>
        <section id="b_1" method="create">
            <user id="b_1c">
                <attribute>a</attribute>
            </user>
        </section>
        <section id="b_2">
            <user id="b_1a">
                <attribute>
                    <name>John</name>
                </attribute>
            </user>
        </section>
    </node>
</root>

产生了想要的正确结果:

<root>
   <node id="a">
      <section id="a_1" method="run">
         <item id="0">
            <attribute>
               <color>Red</color>
            </attribute>
         </item>
      </section>
      <section id="a_2">
         <item id="0">
            <attribute>
               <color>Red</color>
            </attribute>
         </item>
      </section>
   </node>
   <node id="b">
      <section id="b_1" method="create">
         <user id="b_1a">
            <attribute>
               <name>John</name>
            </attribute>
         </user>
         <user id="b_1b">
            <attribute>a</attribute>
         </user>
      </section>
      <section id="b_2">
         <user id="b_1a">
            <attribute>
               <name>John</name>
            </attribute>
         </user>
      </section>
   </node>
</root>

<强>解释

Muenchian method for grouping 与复合键一起使用。在这里,我们忽略(删除)不是组中第一个节点的每个节点。


<强> II。 XSLT 2.0解决方案 - 更短,效率更低:

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="node()|@*">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match="node">
  <xsl:copy>
    <xsl:apply-templates select="@*"/>

    <xsl:for-each-group select="*"
         group-by="concat(name(), '+', @id, '+', @method)">
      <xsl:apply-templates select="."/>
    </xsl:for-each-group>
  </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

<强>解释

正确使用带有group-by属性的 xsl:for-each-group

答案 1 :(得分:1)

XSL文件:

<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/">
    <xsl:apply-templates/>
</xsl:template>

<xsl:template match="root">
    <root>
        <xsl:apply-templates/>
    </root>
</xsl:template>

<xsl:template match="node">
    <node>
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates/>
    </node>
</xsl:template>

<xsl:template match="section[@id = 'b_1'][1]">  
    <section>
        <xsl:copy-of select="node()|@*"/>
    </section>  
</xsl:template>

<xsl:template match="section[@id != 'b_1']">
    <section>       
        <xsl:copy-of select="node()|@*"/>
    </section>
</xsl:template>

<xsl:template match="section[@id = 'b_1'][position() &gt; 1]"/> 

</xsl:stylesheet>

转型:

<root>
        <node id="a">
            <section id="a_1">
                <item id="0">
                    <attribute>
                        <color>Red</color>
                    </attribute>
                </item>
            </section>
            <section id="a_2">
                <item id="0">
                    <attribute>
                        <color>Red</color>
                    </attribute>
                </item>
            </section>
        </node>
        <node id="b">
            <section id="b_1" method="create">
                <user id="b_1a">
                    <attribute>
                        <name>John</name>
                    </attribute>
                </user>
                <user id="b_1b">
                    <attribute>a</attribute>
                </user>
            </section>
            <section id="b_2">
                <user id="b_1a">
                    <attribute>
                        <name>John</name>
                    </attribute>
                </user>
            </section>
        </node>
    </root>

希望这会有所帮助。

[编辑] 在同一输入文件上试用此XSL:

<?xml version='1.0'?>
<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

    <xsl:template match="/">
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="*[not(@id eq preceding::*[local-name() eq local-name(.)]/@id)]">
        <xsl:element name="{name()}">
            <xsl:copy-of select="node()|@*"/>
        </xsl:element>
    </xsl:template>

</xsl:stylesheet>

,结果:

<root>
  <node id="a">
    <section id="a_1">
      <item id="0">
        <attribute>
          <color>Red</color>
        </attribute>
      </item>
    </section>
    <section id="a_2">
      <item id="0">
        <attribute>
          <color>Red</color>
        </attribute>
      </item>
    </section>
  </node>
  <node id="b">
    <section id="b_1" method="create">
      <user id="b_1a">
        <attribute>
          <name>John</name>
        </attribute>
      </user>
      <user id="b_1b">
        <attribute>a</attribute>
      </user>
    </section>
    <section id="b_1" method="create">
      <user id="b_1c">
        <attribute>a</attribute>
      </user>
    </section>
    <section id="b_2">
      <user id="b_1a">
        <attribute>
          <name>John</name>
        </attribute>
      </user>
    </section>
  </node>
</root>

[/编辑]