【问题标题】:XSL - remove the duplicate node but keep the originalXSL - 删除重复节点但保留原始节点
【发布时间】:2013-04-10 11:10:51
【问题描述】:

在使用 XSLT 从输入 xml 中删除重复节点时需要帮助

这就是我的 XML 的样子,

<?xml version="1.0"?>

<NodeA NodeAattr="123">

<NodeB NodeBattr="456"></NodeB>

<NodeC>
    <NodeD="ValueD">
       <NodeE Name="ValueABC">
          <NodeF Value="0"></NodeF >
       </NodeE >
       <NodeE Name="ValueABC">
          <NodeF Value="0"></NodeF >
       </NodeE>
  </NodeD>
</NodeC>
</NodeA>

我的最终输出应该是这样的

<NodeA NodeAattr="123">

<NodeB NodeBattr="456"></NodeB>

<NodeC>
    <NodeD="ValueD">
       <NodeE Name="ValueABC">
          <NodeF Value="0"></NodeF>
       </NodeE >
    </NodeD>
</NodeC>
</NodeA>

这里节点 E 的 Name 属性有重复值。基于这个属性,我需要消除重复。

如果有人可以帮助我使用此处所需的 XSLT 以获得输出,那将非常有帮助。 我只能使用 XSLT 1.0

【问题讨论】:

  • 重复元素是否总是有相同的父元素?如果两个 NodeE 元素具有相同的名称,但不同的父元素,这是否算作重复?
  • @Tim C 是的,重复的元素将具有相同的父元素

标签: xml xslt xslt-1.0 duplicate-removal


【解决方案1】:

如果两个 &lt;NodeE&gt; 元素只有在它们具有相同的父元素时才被认为是重复的,这可能是最简单的解决方案:

输入

<?xml version="1.0"?>

<NodeA NodeAattr="123">

  <NodeB NodeBattr="456"></NodeB>

  <NodeC>
    <NodeD Name="ValueD">
      <NodeE Name="ValueABC">
        <NodeF Value="0"></NodeF>
      </NodeE>
      <NodeE Name="ValueABC">
        <NodeF Value="0"></NodeF>
      </NodeE>
    </NodeD>
    <!-- Added another <NodeD> element for demonstration -->
    <NodeD>
      <NodeE Name="ValueABC">
        <NodeF Value="0"></NodeF>
      </NodeE>
      <NodeE Name="ValueDEF">
        <NodeF Value="0"></NodeF>
      </NodeE>
    </NodeD>
  </NodeC>
</NodeA>

样式表#1

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

  <!--
  Identity transform: copy elements and attributes from input file as is
  -->
  <xsl:template match="node() | @*">
    <xsl:copy>
      <xsl:apply-templates select="node() | @*"/>
    </xsl:copy>
  </xsl:template>

  <!--
  Drop <NodeE> elements with a preceding <NodeE> sibling that has the same
  @Name attribute value as the current element
  -->
  <xsl:template
    match="NodeE[preceding-sibling::NodeE[@Name = current()/@Name]]"/>

</xsl:stylesheet>

输出 #1

<?xml version="1.0" encoding="utf-8"?>
<NodeA NodeAattr="123">
  <NodeB NodeBattr="456"/>
  <NodeC>
    <NodeD Name="ValueD">
      <NodeE Name="ValueABC">
        <NodeF Value="0"/>
      </NodeE>
    </NodeD>
    <NodeD>
      <NodeE Name="ValueABC">
        <NodeF Value="0"/>
      </NodeE>
      <NodeE Name="ValueDEF">
        <NodeF Value="0"/>
      </NodeE>
    </NodeD>
  </NodeC>
</NodeA>

另一方面,如果&lt;NodeE&gt; 元素应被视为在整个文档中重复,则可以使用 Muenchian 分组:

样式表#2

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

  <xsl:key name="kNode" match="NodeE" use="@Name"/>

  <!--
  Identity transform: copy elements and attributes from input file as is
  -->
  <xsl:template match="node() | @*">
    <xsl:copy>
      <xsl:apply-templates select="node() | @*"/>
    </xsl:copy>
  </xsl:template>

  <!--
  Use Muenchian grouping to apply unique NodeE elements.
  See http://www.jenitennison.com/xslt/grouping/muenchian.html
  -->
  <xsl:template match="NodeE[generate-id() = 
                       generate-id(key('kNode', @Name)[1])]">
    <xsl:copy>
      <xsl:apply-templates select="node() | @*"/>
    </xsl:copy>
  </xsl:template>

  <!-- Drop other <NodeE> elements -->
  <xsl:template match="NodeE"/>

</xsl:stylesheet>

输出#2

<?xml version="1.0" encoding="utf-8"?>
<NodeA NodeAattr="123">
  <NodeB NodeBattr="456"/>
  <NodeC>
    <NodeD Name="ValueD">
      <NodeE Name="ValueABC">
        <NodeF Value="0"/>
      </NodeE>
    </NodeD>
    <NodeD>
      <NodeE Name="ValueDEF">
        <NodeF Value="0"/>
      </NodeE>
    </NodeD>
  </NodeC>
</NodeA>

【讨论】:

  • @Eero 它就像一个魅力。万分感谢。但我还有一个问题。例如,如果我只想删除“Name”属性值为“ValueABC”的重复节点,如何重组?我不想触及“名称”属性值为“ValueDEF”、“ValueGHJ”的其他节点
  • 对于样式表 #1,更改非常简单,只需添加一个谓词来缩小节点集:&lt;xsl:template match="NodeE[@Name = 'ValueABC'][preceding-sibling::NodeE[@Name = current()/@Name]]"/&gt;。对于#2,你可以做一些事情like this。 #2 可能有一个更简单的解决方案,但这是我首先想到的。
  • @arulapp:很高兴我能帮上忙!如果您觉得我的回答有用,请考虑accepting
  • 在类似的说明中,您能否也向我提供您在这方面的 cmets 吗? stackoverflow.com/questions/16020026/…
【解决方案2】:

使用“deep-euql”函数比较两个 item()。检查这个:

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

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

  <xsl:template match="NodeE">
    <xsl:choose>
      <xsl:when test="deep-equal(self::NodeE, following-sibling::NodeE)"/>
      <xsl:otherwise>
        <xsl:copy>
          <xsl:apply-templates select="* | @*"/>
        </xsl:copy>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

【讨论】:

  • 我不认为 deep-equal 在 XSLT 1.0 中可用。
  • @Peter 是正确的:deep-equal() 仅在 XPath 2.0 中可用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-12-28
  • 2021-06-11
  • 1970-01-01
  • 2023-03-11
  • 2020-08-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多