根据属性值和多个节点的innertext删除xml中的重复项

时间:2013-12-04 13:57:21

标签: xml xslt duplicates filtering

我有以下输入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>

所以我想要做的是确定所有具有相同属性但基数的childelements(即此处level1和level2可能更高的级别)和seg1和seg2的相同innertext,只保留第一个具有最高的childelements seg3(在这种情况下是id = 646的那个并过滤掉id = 648的那个)。

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

感谢您阅读

2 个答案:

答案 0 :(得分:2)

这是一个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处理器,那么<​​/ p>

<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的版本。

答案 1 :(得分:-1)

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>