使用XSLT根据多个属性从XML中删除重复节点

3
我有一个需要使用XSLT进行转换的输入XML。
<root>
    <node id="a">
        <section id="a_1" method="run">
            <item id="0">
                <attribute>
                    <color>Red</color>
                </attribute>
            </item>
        </section>
        <section id="a_2">
            <item id="0">
                <attribute>
                    <color>Red</color>
                </attribute>
            </item>
        </section>
        <section id="a_1" method="run">
            <item id="0">
                <attribute>
                    <color>Red</color>
                </attribute>
            </item>
        </section>
    </node>
    <node id="b">
        <section id="b_1" method="create">
            <user id="b_1a">
                <attribute>
                    <name>John</name>
                </attribute>
            </user>
            <user id="b_1b">
                <attribute>a</attribute>
            </user>
        </section>
        <section id="b_1" method="create">
            <user id="b_1c">
                <attribute>a</attribute>
            </user>
        </section>
        <section id="b_2">
            <user id="b_1a">
                <attribute>
                    <name>John</name>
                </attribute>
            </user>
        </section>
    </node>
</root>

预期输出:

<root>
    <node id="a">
        <section id="a_1">
            <item id="0">
                <attribute>
                    <color>Red</color>
                </attribute>
            </item>
        </section>
        <section id="a_2">
            <item id="0">
                <attribute>
                    <color>Red</color>
                </attribute>
            </item>
        </section>
    </node>
    <node id="b">
        <section id="b_1" method="create">
            <user id="b_1a">
                <attribute>
                    <name>John</name>
                </attribute>
            </user>
            <user id="b_1b">
                <attribute>a</attribute>
            </user>
        </section>

        <section id="b_2">
            <user id="b_1a">
                <attribute>
                    <name>John</name>
                </attribute>
            </user>
        </section>
    </node>
</root>

无论删除哪个节点,只要它具有相同的元素名称、ID和方法,其中一个将被删除。 你知道xsl是什么样子吗?

注意:元素名称可以是任何东西,不一定是<p>,整个文件中有多个元素名称;只要具有相同的元素名称、ID和属性(例如method=create),其中一个将被删除。

非常感谢。 祝好, 约翰

2个回答

3

I. 这是一个简短而高效(使用键)的XSLT 1.0转换:

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

 <xsl:key name="kElemWithAttribs" match="*[@id and @method]"
  use="concat(name(), '+', @id, '+', @method)"/>

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

 <xsl:template match=
  "*[@id and @method
    and
     not(generate-id()
        =
         generate-id(key('kElemWithAttribs',
                         concat(name(), '+', @id, '+', @method)
                         )[1]
                    )
         )
     ]"/>
</xsl:stylesheet>

当应用此转换到提供的 XML 文档时:
<root>
    <node id="a">
        <section id="a_1" method="run">
            <item id="0">
                <attribute>
                    <color>Red</color>
                </attribute>
            </item>
        </section>
        <section id="a_2">
            <item id="0">
                <attribute>
                    <color>Red</color>
                </attribute>
            </item>
        </section>
        <section id="a_1" method="run">
            <item id="0">
                <attribute>
                    <color>Red</color>
                </attribute>
            </item>
        </section>
    </node>
    <node id="b">
        <section id="b_1" method="create">
            <user id="b_1a">
                <attribute>
                    <name>John</name>
                </attribute>
            </user>
            <user id="b_1b">
                <attribute>a</attribute>
            </user>
        </section>
        <section id="b_1" method="create">
            <user id="b_1c">
                <attribute>a</attribute>
            </user>
        </section>
        <section id="b_2">
            <user id="b_1a">
                <attribute>
                    <name>John</name>
                </attribute>
            </user>
        </section>
    </node>
</root>

期望的、正确的结果已生成:

<root>
   <node id="a">
      <section id="a_1" method="run">
         <item id="0">
            <attribute>
               <color>Red</color>
            </attribute>
         </item>
      </section>
      <section id="a_2">
         <item id="0">
            <attribute>
               <color>Red</color>
            </attribute>
         </item>
      </section>
   </node>
   <node id="b">
      <section id="b_1" method="create">
         <user id="b_1a">
            <attribute>
               <name>John</name>
            </attribute>
         </user>
         <user id="b_1b">
            <attribute>a</attribute>
         </user>
      </section>
      <section id="b_2">
         <user id="b_1a">
            <attribute>
               <name>John</name>
            </attribute>
         </user>
      </section>
   </node>
</root>

说明:

使用具有复合键的Muenchian方法进行分组。在这里,我们忽略(删除)不是一组中第一个节点的所有节点。


II. XSLT 2.0解决方案--更短,但同样有效:

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>

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

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

    <xsl:for-each-group select="*"
         group-by="concat(name(), '+', @id, '+', @method)">
      <xsl:apply-templates select="."/>
    </xsl:for-each-group>
  </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

说明:

使用带有 group-by 属性的 xsl:for-each-group 的正确方法。


还有一件事..如果只有在相同的父级(部分ID)下才能删除重复项,那么如何修复此代码?谢谢。 - John
@John:你能否提出一个新问题,包括源XML文档和所需结果,并解释新的要求?然后通知我,我很乐意回答它。 - Dimitre Novatchev
我马上会发布并通知您。感谢您的时间。 - John
对不起,这个问题可能很复杂。我实在没有头绪。感谢您的帮助。以下是问题链接:http://stackoverflow.com/questions/10274305/remove-the-same-node-with-same-attribute-under-the-same-parent-in-xml-using-xslt - John

1

XSL文件:

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

<xsl:template match="/">
    <xsl:apply-templates/>
</xsl:template>

<xsl:template match="root">
    <root>
        <xsl:apply-templates/>
    </root>
</xsl:template>

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

<xsl:template match="section[@id = 'b_1'][1]">  
    <section>
        <xsl:copy-of select="node()|@*"/>
    </section>  
</xsl:template>

<xsl:template match="section[@id != 'b_1']">
    <section>       
        <xsl:copy-of select="node()|@*"/>
    </section>
</xsl:template>

<xsl:template match="section[@id = 'b_1'][position() &gt; 1]"/> 

</xsl:stylesheet>

转换:

<root>
        <node id="a">
            <section id="a_1">
                <item id="0">
                    <attribute>
                        <color>Red</color>
                    </attribute>
                </item>
            </section>
            <section id="a_2">
                <item id="0">
                    <attribute>
                        <color>Red</color>
                    </attribute>
                </item>
            </section>
        </node>
        <node id="b">
            <section id="b_1" method="create">
                <user id="b_1a">
                    <attribute>
                        <name>John</name>
                    </attribute>
                </user>
                <user id="b_1b">
                    <attribute>a</attribute>
                </user>
            </section>
            <section id="b_2">
                <user id="b_1a">
                    <attribute>
                        <name>John</name>
                    </attribute>
                </user>
            </section>
        </node>
    </root>

希望这可以帮到你。
[编辑] 在相同的输入文件上尝试使用这个 XSL:
<?xml version='1.0'?>
<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

    <xsl:template match="/">
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="*[not(@id eq preceding::*[local-name() eq local-name(.)]/@id)]">
        <xsl:element name="{name()}">
            <xsl:copy-of select="node()|@*"/>
        </xsl:element>
    </xsl:template>

</xsl:stylesheet>

结果如下:

<root>
  <node id="a">
    <section id="a_1">
      <item id="0">
        <attribute>
          <color>Red</color>
        </attribute>
      </item>
    </section>
    <section id="a_2">
      <item id="0">
        <attribute>
          <color>Red</color>
        </attribute>
      </item>
    </section>
  </node>
  <node id="b">
    <section id="b_1" method="create">
      <user id="b_1a">
        <attribute>
          <name>John</name>
        </attribute>
      </user>
      <user id="b_1b">
        <attribute>a</attribute>
      </user>
    </section>
    <section id="b_1" method="create">
      <user id="b_1c">
        <attribute>a</attribute>
      </user>
    </section>
    <section id="b_2">
      <user id="b_1a">
        <attribute>
          <name>John</name>
        </attribute>
      </user>
    </section>
  </node>
</root>

[/编辑]


谢谢您的回复,但正如我在问题中提到的,我们只需要删除一个重复的节点。所以在这种情况下,我们仍然需要保留 <section id="b_1" method="create"> 及其相应的子节点。您介意更新解决方案吗?非常感谢。 - John
谢谢@Cylian!非常感谢。只有一个问题,是否可能进行泛化,我的意思是它可以是任何元素,不一定是<section>,只要元素名称、ID和属性(method=create)相同,其中一个将被消除。 - John
你使用的是哪个XSLT版本? - Cylian
请查看此帖子此处 - Cylian
它不同于没有属性。 - John

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接