使用XSLT删除连续的重复项

时间:2011-11-11 03:12:10

标签: xml xslt xpath

我有一些XML,我希望删除相同的连续子节点,这些子节点位于不同的父节点中。也就是说,如果一个孩子(在不同的父母)节点我的XML树连续出现两次或更多次,我想删除所有重复项。

我想到的重复节点是前两个<child>a</child>节点中的<parent>

一个例子:

以下是源XML:

<root>
   <parent>
      <child>a</child>
      <child>b</child>
      <child>c</child>
   </parent>

   <parent>
      <child>a</child>
      <child>bb</child>
      <child>cc</child>
   </parent>

   <parent>
      <child>aaa</child>
      <child>bbb</child>
      <child>ccc</child>
   </parent>

   <parent>
      <child>a</child>
      <child>bbbb</child>
      <child>cccc</child>
   </parent>

</root>

这是所需的XML:

<root>
   <parent>
      <child>a</child>
      <child>b</child>
      <child>c</child>
   </parent>

   <parent>
      <child>bb</child>
      <child>cc</child>
   </parent>

   <parent>
      <child>aaa</child>
      <child>bbb</child>
      <child>ccc</child>
   </parent>

   <parent>
      <child>a</child>
      <child>bbbb</child>
      <child>cccc</child>
   </parent>

</root>

只删除了一个元素,但如果开头有5个连续<child>a</child>个节点(而不是2个),则会删除其中的4个节点。我正在使用XSLT 2.0。

我感谢任何帮助。

后续:

感谢Kirill,我得到了我想要的文档,但如果我有这样的XML文档,这会产生一个我没想到的新问题:

<root>
   <parent>
      <child>a</child>
      <child>b</child>
      <child>c</child>
   </parent>

   <parent>
      <child>a</child>
      <child>b</child>
      <child>c</child>
   </parent>

   <parent>
      <child>aaa</child>
      <child>bbb</child>
      <child>ccc</child>
   </parent>

</root>

我申请Kirill的XSLT,我明白了:

<root>
   <parent>
      <child>a</child>
      <child>b</child>
      <child>c</child>
   </parent>

   <parent>
   </parent>

   <parent>
      <child>aaa</child>
      <child>bbb</child>
      <child>ccc</child>
   </parent>

</root>

如何删除<parent> </parent>?对于我的应用程序,可能还有<parent>的其他子元素,如果<child>元素中没有<parent>元素,则可以删除它们。

我有一个我不喜欢的解决方案是在第一个之后应用另一个转换。这仅在按顺序应用时才有效,我需要一个单独的XSLT文件,需要运行两个命令而不是一个。

这是:

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

 <xsl:template match="parent[not(child)]"/>

3 个答案:

答案 0 :(得分:3)

使用:

<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="child[../preceding-sibling::parent[1]/child = .]"/>

</xsl:stylesheet>

答案 1 :(得分:2)

如果您能够使用XSLT 2.0,则问题解决如下:

<xsl:for-each-group select="parent" group-adjacent="child[1]">
  <xsl:for-each select="current-group()">
    <parent>
      <xsl:if test="position()=1">
        <xsl:copy-of select="current-group()[1]/child[1]"/>
      </xsl:if>
      <xsl:copy-of select="current-group()/child[position() gt 1]"/>
    </parent>
  </xsl:for-each>
</xsl:for-each-group>

答案 2 :(得分:0)

这回答了新增的后续问题

  

如何删除<parent> </parent>?我在那里申请   可能是<parent>的其他子元素,如果可以删除   元素中没有<child>元素。

这个转换是Kirill的一个附加组件,可以完成所需的空parent元素的清理,而不需要第二遍

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

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

  <xsl:template match="child[../preceding-sibling::parent[1]/child = .]"/>

  <xsl:template match=
  "parent
     [not(child
          [not(. = ../preceding-sibling::parent[1]
                                              /child
               )
           ]
          )
     ]"/>
</xsl:stylesheet>

应用于提供的XML文档

<root>
   <parent>
      <child>a</child>
      <child>b</child>
      <child>c</child>
   </parent>

   <parent>
      <child>a</child>
      <child>b</child>
      <child>c</child>
   </parent>

   <parent>
      <child>aaa</child>
      <child>bbb</child>
      <child>ccc</child>
   </parent>

</root>

产生了想要的正确结果

<root>
  <parent>
    <child>a</child>
    <child>b</child>
    <child>c</child>
  </parent>
  <parent>
    <child>aaa</child>
    <child>bbb</child>
    <child>ccc</child>
  </parent>
</root>