如何使用xslt删除xml文件中的重复项?

时间:2013-04-19 10:06:58

标签: java xml xslt xml-parsing xslt-1.0

示例xml如下:

   <?xml version="1.0" encoding="UTF-8" standalone="no"?>
       <check>
        <val>
          <Samsung>
             <name value="galaxy"/>
             <name value="galaxy"/>
             <name value="galaxys"/>
             <id value="123"/>
             <id value="123"/>
             <name2 value="galaxy"/>
           </Samsung>

           <htc>
             <name value="galaxy"/>
             <name value="galaxy"/>
             <name value="galaxys"/>
             <id value="123"/>
             <id value="123"/>
             <name2 value="galaxy"/>
          </htc>
        </val>
     </check>

如何删除重复项?

带有匹配值的

<name><id>标签...如果还有除<Samsung><htc>以外的其他标签,如何在xslt中编写循环?我不知道怎么写xslt。请帮忙。

输出xml应如下所示:

<check>
    <val>
      <Samsung>
         <name value="galaxy"/>
         <name value="galaxys"/>
         <id value="123"/>
         <name2 value="galaxy"/>
       </Samsung>

       <htc>
         <name value="galaxy"/>
         <name value="galaxys"/>
         <id value="123"/>
         <name2 value="galaxy"/>
      </htc>
    </val>
 </check>

3 个答案:

答案 0 :(得分:2)

如果你能确保重复的节点总是连续的,那么最简单的方法就是建立在XSTL身份转换的基础上,只需要一个额外的模板去除模板就好了

<xsl:template 
     match="*[not(*)]
             [name() = preceding-sibling::*[1]/name()]
             [@value = preceding-sibling::*[1]/@value]" />

这匹配任何子元素,如果它与前一个元素具有相同的名称和值,则忽略它。在这种情况下,无需在任何地方对元素名称进行硬编码。

在这种情况下,这是完整的XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes" />

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

  <xsl:template match="*[not(*)][name() = preceding-sibling::*[1]/name()][@value = preceding-sibling::*[1]/@value]" />
</xsl:stylesheet>

但是,如果您的XML看起来像这样,并且您的重复节点是连续的

,则会失败
       <Samsung>
         <name value="galaxy"/>
         <name value="galaxys"/>
         <id value="123"/>
         <name value="galaxy"/>
         <id value="123"/>
         <name2 value="galaxy"/>
       </Samsung>

您可以通过更改模板来检查所有以前的节点

来解决此问题
<xsl:template match="*[not(*)]
                      [name() = preceding-sibling::*/name()]
                      [@value = preceding-sibling::*/@value]" />

然而,随着大量元素的出现,这开始变得效率低下。如果你有数百个元素,那么每个precedinig-sibling检查将重复涉及检查数百个元素(即第100个元素必须检查99个前面的元素,第101个元素检查100个元素等)。

更有效的方法(在XSLT1.0中)是使用一种名为Muenchian Grouping的技术。如果你经常使用XSLT,肯定值得学习。

首先,您定义一个键,以“组合”您的元素。在这种情况下,您正在寻找由其父元素名称和值

定义的不同元素
<xsl:key name="duplicate" match="*[not(*)]" use="concat(generate-id(..), '|', name(), '|', @value)" />

然后忽略重复项,匹配给定'lookup'值的键中第一个位置没有出现的任何元素

<xsl:template match="*[not(*)]
                      [generate-id() != 
                      generate-id(key('duplicate', concat(generate-id(..), '|', name(), '|', @value))[1])]" />

在这种情况下,这是完整的XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes" />
    <xsl:key name="duplicate" match="*[not(*)]" use="concat(generate-id(..), '|', name(), '|', @value)" />

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

 <xsl:template match="*[not(*)][generate-id() != generate-id(key('duplicate', concat(generate-id(..), '|', name(), '|', @value))[1])]" />

</xsl:stylesheet>

答案 1 :(得分:1)

进行此转换时

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

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

<xsl:template match="name">
<xsl:if test="self::name/text()= following-sibling::name/text()">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:if>
</xsl:template>
<xsl:template match="name2">
<xsl:if test="self::name2/text()= following-sibling::name2/text()">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

在XML 下面运行

<?xml version="1.0"?>
<check>
 <val>
    <sai>
     <name> A</name>
     <name> A</name>
    <name2> B</name2>
    <name2> B</name2>
   </sai>
   <dinesh>
    <name> A</name>
    <name> A</name>
    <name2> B</name2>
    <name2> B</name2>
    </dinesh>   
 </val> 
</check>

获取所需的输出

<?xml version='1.0' ?>
<check> 
  <val>    
    <sai>     
      <name> A</name>       
      <name2> B</name2>
    </sai>
    <dinesh>
      <name> A</name>
      <name2> B</name2>
    </dinesh>   
   </val> 
</check>

答案 2 :(得分:0)

与@ siva2012的回答差不多。但如果只有一个名字孩子,那就更正确了。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">
    <xsl:output method="xml" />

    <xsl:template match="name">
        <xsl:variable name="text" select="text()"/>
        <xsl:if test="not(following-sibling::name[text()= $text])" >
            <xsl:copy>
                <xsl:apply-templates select="node()|@*" />
            </xsl:copy>
        </xsl:if>
    </xsl:template>
        <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*" />
        </xsl:copy>
    </xsl:template>
    <xsl:template match="/">
        <xsl:apply-templates  />

    </xsl:template>
</xsl:stylesheet>