如何使用xslt从xml中删除重复的节点

时间:2016-05-06 13:39:36

标签: xml xslt

我尝试了多个选项来从xml中删除重复的节点但是无法实现正确的结果。下面是输入xml

<root>
  <paymentFlag>true</paymentFlag>
  <cardFlag>true</cardFlag>
  <pinFlag>true</pinFlag>
  <paymentFlag>true</paymentFlag>
  <outputFlag>false</outputFlag>
  <pinFlag>true</pinFlag>
  <cardFlag>true</cardFlag>
  ...
</root>

在根元素内部,我有n个节点都在同一级别,但有些节点是重复的。我想删除它们并期望低于输出。

<root>
  <paymentFlag>true</paymentFlag>
  <cardFlag>true</cardFlag>
  <pinFlag>true</pinFlag>
  <outputFlag>false</outputFlag>
</root>

请提供您的意见。

4 个答案:

答案 0 :(得分:0)

您可以使用<xsl:key>生成一个地图,将第一次出现与其他事件分开:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="xml" indent="yes" />
  <xsl:key name="firsts" match="/root/*" use="local-name()" />    
  <xsl:template match="/root"> 
    <root>
      <xsl:for-each select="*[generate-id() = generate-id(key('firsts',local-name()))]">
        <xsl:copy-of select="." />
      </xsl:for-each>
    </root>
  </xsl:template>
</xsl:stylesheet>

输出

<?xml version="1.0"?>
<root>
    <paymentFlag>true</paymentFlag>
    <cardFlag>true</cardFlag>
    <pinFlag>true</pinFlag>
    <outputFlag>false</outputFlag>
</root>

确定我使用的元素的全名local-name(),因为大多数初学者不关心 namespace ,并且不考虑:之前的部分QName如abc:Element。因此,如果您需要考虑namespace,请将local-name()替换为name()

答案 1 :(得分:0)

这是一个使用xsl:key的XSLT 1.0选项,与currently accepted answer非常相似。

不同之处在于,如果您从XSLT 1.0更改为2.0,则此答案不会中断。它适用于两个版本。 (另一个答案将失败,错误&#34; A sequence of more than one item is not allowed as the first argument of generate-id()&#34;如果以2.0版本运行。)

另一个区别是我使用name()代替local-name()。通过这样做,我将具有名称空间前缀的元素视为不同(它们是)。如果您想对它们进行相同处理,请​​使用local-name()。 (另外,如果你想处理不同默认命名空间中的元素(没有前缀),你可以使用namespace-uri()创建一个复合键。如果你想要一个例子,请告诉我。)

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

  <xsl:key name="firsts" match="/*/*" use="name()"/>

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

  <xsl:template match="/*">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <xsl:for-each select="*[count(.|key('firsts',name())[1])=1]">
        <xsl:apply-templates select="."/>
      </xsl:for-each>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

如果您已经在使用2.0,那么使用xsl:for-each-group这是另一个仅限2.0的选项......

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

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

  <xsl:template match="/*">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <xsl:for-each-group select="*" group-by="name()">
        <xsl:apply-templates select="current-group()[1]"/>
      </xsl:for-each-group>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

请注意,我在两个选项中都使用了xsl:apply-templates,以便日后更容易扩展样式表。

答案 2 :(得分:-1)

  

相同级别,但其中一些是重复的

问题是什么被认为是重复的。如果只检查元素名称,您可以尝试这样的事情:

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

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

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

</xsl:stylesheet>

答案 3 :(得分:-1)

使用xsl样式表版本2

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />
  <xsl:template match="/">
    <root>
      <xsl:for-each select="root/*[not(name()=preceding::*/name())]">
        <xsl:element name="{name()}">
              <xsl:value-of select="text()"/>
        </xsl:element>
      </xsl:for-each>
    </root>
  </xsl:template>
</xsl:stylesheet>

输出:

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <paymentFlag>true</paymentFlag>
   <cardFlag>true</cardFlag>
   <pinFlag>true</pinFlag>
   <outputFlag>false</outputFlag>
</root>