使用 XSLT 删除重复标记及其在 XML 中的子项

Posted

技术标签:

【中文标题】使用 XSLT 删除重复标记及其在 XML 中的子项【英文标题】:Remove Duplicated Tag and it's childs in XML using XSLT 【发布时间】:2020-11-22 09:31:29 【问题描述】:

所以,我想从我的 xml 中删除所有重复的标签及其子标签(如果存在)。这个例子来自这里How to remove duplicate xml-nodes using xslt? 这与我的问题几乎相同,除了解决方案对我不起作用而且我不知道为什么。

示例:xml 看起来像:

<root>
       <row>
            <title>The Oscars Opening Ceremony: Live from the Red Carpet</title>  <!-- here -->
            <actors>Margot Robbie</actors>
            <actors>Kumail Nanjiani</actors>
            <actors>Timothée Chalamet</actors>
            <actors>Matthew McConaughey</actors>
            <actors>Nicole Kidman</actors>
            <actors>Saoirse Ronan</actors>
            <actors>Jennifer Garner</actors>
            <actors>Armie Hammer</actors>
            <actors>Sandra Bullock</actors>
            <actors>Gary Oldman</actors>
            <actors>Mira Sorvino</actors>
            <actors>Salma Hayek</actors>
            <actors>Mahershala Ali</actors>
            <actors>Jordan Peele</actors>
            <actors>Wendi McLendon-Covey</actors>
            <description>The Oscars Opening</description>
        </row>
       <row>
            <title>Tabaluga tivi</title>
            <actors>Ben Bledsoe</actors>
            <actors>Philipp Wimmer</actors>
            <actors>Patrick King Jr.</actors>
            <description>Tabaluga tivi</description>
        </row>
        <row>
            <title>Library of God</title>
            <actors>Peter Førde</actors>
            <actors>Lasse Vermeli</actors>
            <actors>Hilde Amundsen</actors>
            <description>Library of God</description>
        </row>
        <row>
            <title>The Oscars Opening Ceremony: Live From The Red Carpet</title> <!-- here again -->
            <actors>Mel Gibson</actors>
            <actors>Dwayne Johnson</actors>
            <actors>Nicole Kidman</actors>
            <actors>Robin Roberts</actors>
            <actors>Meryl Streep</actors>
            <actors>Justin Timberlake</actors>
            <description>Interviews with nominees, presenters and performers arriving for the awards ceremony; hosts Robin Roberts, Michael Strahan and Lara Spencer.</description>
        </row>
</root>

理想的输出结果:

<root>
        <row>
            <title>The Oscars Opening Ceremony: Live from the Red Carpet</title>  <!-- only this one at result -->
            <actors>Margot Robbie</actors>
            <actors>Kumail Nanjiani</actors>
            <actors>Timothée Chalamet</actors>
            <actors>Matthew McConaughey</actors>
            <actors>Nicole Kidman</actors>
            <actors>Saoirse Ronan</actors>
            <actors>Jennifer Garner</actors>
            <actors>Armie Hammer</actors>
            <actors>Sandra Bullock</actors>
            <actors>Gary Oldman</actors>
            <actors>Mira Sorvino</actors>
            <actors>Salma Hayek</actors>
            <actors>Mahershala Ali</actors>
            <actors>Jordan Peele</actors>
            <actors>Wendi McLendon-Covey</actors>
            <description>The Oscars Opening</description>
        </row>
       <row>
            <title>Tabaluga tivi</title>
            <actors>Ben Bledsoe</actors>
            <actors>Philipp Wimmer</actors>
            <actors>Patrick King Jr.</actors>
            <description>Tabaluga tivi</description>
        </row>
        <row>
            <title>Library of God</title>
            <actors>Peter Førde</actors>
            <actors>Lasse Vermeli</actors>
            <actors>Hilde Amundsen</actors>
            <description>Library of God</description>
        </row>
   </root>

这是我正在使用的 xslt:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="kTitleByContent" match="row"
             use="concat(title, '+', actors, '+', description)"/>
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="row[generate-id() !=
                              generate-id(key('kTitleByContent',
                                              concat(title,'+',
                                                     actors,'+',
                                                     description))[1])]"/>
</xsl:stylesheet>

为什么不删除重复的?感谢任何帮助。谢谢

【问题讨论】:

也许只使用title 作为密钥?目前尚不清楚您希望使用连接键值实现什么,您似乎有多个不同顺序的 actors 元素。 @MartinHonnen 我想删除一个 并且它是孩子,每次我发现 的重复值。整个 XML 必须只有唯一的 <row><title>... 您只需要在键声明中使用use="title",但是请注意,对于您的示例,就纯粹的、不区分大小写的字符串比较而言,标题不是重复的,因为有Live from the Red 和另一个Live from The Red。因此,您需要在 XSLT 2 及更高版本中额外使用小写字母,或者编写一个长 translate 调用,将 XSLT 1 中的所有字母转换为小写字母。 【参考方案1】:

如果您只想比较 title 并且它们完全相等,则只需使用 title 元素即可:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">
    
  <xsl:key name="kRowByTitle" 
           match="row"
           use="title"/>

  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="row[generate-id() != generate-id(key('kRowByTitle', title)[1])]"/>

</xsl:stylesheet>

请注意,但是在您发布的示例中,第一个和第四个 title 的字母大小写有所不同,因此您可能还需要在 XSLT 2 及更高版本中使用 lower-case 函数,或者您需要声明两个参数所有需要处理的大写和小写字母,并使用translate

在 XSLT 3 中它只是

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="3.0">
    
  <xsl:key name="kRowByTitle" 
           match="row"
           use="lower-case(title)"/>

  <xsl:mode on-no-match="shallow-copy"/>
  
  <xsl:template match="row[not(. is key('kRowByTitle', lower-case(title))[1])]"/>

</xsl:stylesheet>

【讨论】:

嘿@MartinHonnen 感谢您的回答!我在这里试过,发生的正是你所说的。您的第一个答案完全删除了标题,但不处理标题中的小写或大写。你的第二个答案,这正是我需要的,它不适合我。我收到此错误:'row[not(.is key('kRowByTitle', lower-case(title))[1])]' 上的 sintaxe 错误。你知道可能出了什么问题吗? @GuilhermeSchults,第二个示例是 XSLT 2/3,仅受 XSLT 2 或 3 处理器(如 Saxon 9 或 10 或 Saxon-JS 2 或 Saxon-C 或 XmlPrime 或 Altova XMLSpy/Raptor)支持。我没有使用 XSLT 1 更新第一个示例,因为 XPath/XSLT 1 中没有小写函数,您需要将您期望的字母存储在两个变量中,一个是所有大写字母的序列,第二个是所有小写​​字母的序列,然后在各处使用translate函数转换为小写进行比较。 所以基本上更新 XSLT 1 示例需要更改 XSLT 结构,因为您可以在 XSLT 1 中使用 match 模式中的变量。也许其他人愿意说明这一点。

以上是关于使用 XSLT 删除重复标记及其在 XML 中的子项的主要内容,如果未能解决你的问题,请参考以下文章

动态添加 XML 标记,并且不应该允许基于使用 XSLT 的 xml 中特定标记的计数重复

如何使用 XSLT 从 XML 中删除元素标记

如何使用 XSLT 正确删除 XML 标记

使用 XSLT 进行空前缀转换的 XML [重复]

XSLT 转换从混合内容中删除 HTML 元素

通过保留第一次出现的节点来删除 XSLT 属性中的重复项