【问题标题】:Remove duplicates in xml based on attributes values and several nodes' innertext根据属性值和几个节点的内部文本删除 xml 中的重复项
【发布时间】:2013-12-04 23:24:51
【问题描述】:

我有以下输入 XML,需要使用 xslt 进行转换

输入 Xml:

<element>
  <childelement xml:type="base" id="645">
    <seg1>a</seg1>
    <seg2>A</seg2>
    <seg3>0</seg3>
  </childelement >
  <childelement xml:type="level1" id="646">
    <seg1>a</seg1>
    <seg2>B</seg2>
    <seg3>1</seg3>
  </childelement>
  <childelement xml:type="level2" id="656">
    <seg1>a</seg1>
    <seg2>C</seg2>
    <seg3>0</seg3>
  </childelement>
</element>
<element>
  <childelement xml:type="base" id="647">
    <seg1>a</seg1>
    <seg2>A</seg2>
    <seg3>0</seg3>
  </childelement>
  <childelement xml:type="level1" id="648">
    <seg1>a</seg1>
    <seg2>B</seg2>
    <seg3>0</seg3>
  </childelement>
  <childelement xml:type="level2" id="649">
    <seg1>a</seg1>
    <seg2>D</seg2>
    <seg3>0</seg3>
  </childelement>
</element>

预期输出:

<element>
  <childelement xml:type="base" id="645">
    <seg1>a</seg1>
    <seg2>A</seg2>
    <seg3>0</seg3>
  </childelement >
  <childelement xml:type="level1" id="646">
        <seg1>a</seg1>
    <seg2>B</seg2>
        <seg3>1</seg3>
   </childelement>
  <childelement xml:type="level2" id="656">
        <seg1>a</seg1>
    <seg2>C</seg2>
        <seg3>0</seg3>
  </childelement>
</element>
<element>
  <childelement xml:type="base" id="647">
        <seg1>a</seg1>
        <seg2>A</seg2>
        <seg3>0</seg3>
      </childelement>
  <childelement xml:type="level2" id="649">
    <seg1>a</seg1>
    <seg2>D</seg2>
    <seg3>0</seg3>
  </childelement>
</element>

所以我想做的是识别所有具有相同属性但基础的子元素(即此处的 level1 和 level2 可能更高级别)以及 seg1 和 seg2 的相同内部文本,只保留第一个最高的seg3(在这种情况下,id = 646 并过滤掉 id = 648)。

xml/xlst(根据属性和内部文本匹配重复项)是否可以进行这样的处理?

感谢阅读

【问题讨论】:

  • 你可以使用 XLST 2.0 吗?

标签: xml xslt duplicates filtering


【解决方案1】:

这是一个 XSLT 2.0 样式表:

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

<xsl:key name="dist" match="childelement[not(@xml:type = 'base')]"
  use="concat(@xml:type, '|', seg1, '|', seg2)"/>

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

<xsl:template match="childelement[not(@xml:type = 'base')][seg3 &lt; max(key('dist', concat(@xml:type, '|', seg1, '|', seg2))/seg3)]"/>

</xsl:stylesheet>

当将 Saxon 9 应用于您的输入样本(添加了 root 元素)时,输出为

<root>
<element>
  <childelement xml:type="base" id="645">
    <seg1>a</seg1>
    <seg2>A</seg2>
    <seg3>0</seg3>
  </childelement>
  <childelement xml:type="level1" id="646">
    <seg1>a</seg1>
    <seg2>B</seg2>
    <seg3>1</seg3>
  </childelement>
  <childelement xml:type="level2" id="656">
    <seg1>a</seg1>
    <seg2>C</seg2>
    <seg3>0</seg3>
  </childelement>
</element>
<element>
  <childelement xml:type="base" id="647">
    <seg1>a</seg1>
    <seg2>A</seg2>
    <seg3>0</seg3>
  </childelement>

  <childelement xml:type="level2" id="649">
    <seg1>a</seg1>
    <seg2>D</seg2>
    <seg3>0</seg3>
  </childelement>
</element>
</root>

如果您需要使用 XSLT 1.0 处理器,那么

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

<xsl:key name="dist" match="childelement[not(@xml:type = 'base')]"
  use="concat(@xml:type, '|', seg1, '|', seg2)"/>

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

<xsl:template match="childelement[not(@xml:type = 'base')]">
  <xsl:variable name="this" select="."/>
  <xsl:for-each select="key('dist', concat(@xml:type, '|', seg1, '|', seg2))">
    <xsl:sort select="seg3" data-type="number" order="descending"/>
    <xsl:if test="position() = 1 and generate-id() = generate-id($this)">
      <xsl:copy-of select="."/>
    </xsl:if>
  </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

是我将 XSLT 2.0 方法翻译成 XSLT 1.0 的版本。

【讨论】:

  • 感谢 Martin 抽出宝贵时间。我正在尝试将它与 c# 控制台应用程序一起使用,但得到错误 'max()' is an unknown XSLT function。
  • 如果您想在 C# 中使用 XSLT 2.0,您可以选择使用 Saxon 9 或 XmlPrime 的 .NET 版本。 Microsoft 的 XslCompiledTransform 不支持 XSLT 2.0。
  • @azazaz,我已经编辑了答案以提供 XSLT 1.0 解决方案
  • 它看起来有效!明天我将尝试完全实施它并让你知道。同时,再次感谢您的宝贵时间。
  • 哦,还有一件事,你相信只用 xpath 也能达到同样的效果吗?
【解决方案2】:

XSLT 1.0 也是如此:

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

<xsl:strip-space elements="*"/>
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>

<xsl:key name="similar" 
         match="childelement[not(@xml:type = 'base')]" 
         use="concat(@xml:type, '|', seg1, '|', seg2)"/>

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

<xsl:template match="childelement[@xml:type!='base']">
    <xsl:variable name="highestId">
        <xsl:for-each select="key('similar', concat(@xml:type, '|', seg1, '|', seg2))">
            <xsl:sort select="seg3" data-type="number" order="descending"/>
            <xsl:if test="position() = 1">
                <xsl:value-of select="@id"/>
            </xsl:if>
        </xsl:for-each>
    </xsl:variable>

    <xsl:if test="@id = $highestId">
        <xsl:copy-of select="."/>
    </xsl:if>
</xsl:template>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-07
    相关资源
    最近更新 更多