我尝试了多个选项来从xml中删除重复的节点但是无法实现正确的结果。下面是输入xml
<root>
<paymentFlag>true</paymentFlag>
<cardFlag>true</cardFlag>
<pinFlag>true</pinFlag>
<paymentFlag>true</paymentFlag>
<outputFlag>false</outputFlag>
<pinFlag>true</pinFlag>
<cardFlag>true</cardFlag>
...
</root>
在根元素内部,我有n个节点都在同一级别,但有些节点是重复的。我想删除它们并期望低于输出。
<root>
<paymentFlag>true</paymentFlag>
<cardFlag>true</cardFlag>
<pinFlag>true</pinFlag>
<outputFlag>false</outputFlag>
</root>
请提供您的意见。
答案 0 :(得分:0)
您可以使用<xsl:key>
生成一个地图,将第一次出现与其他事件分开:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:key name="firsts" match="/root/*" use="local-name()" />
<xsl:template match="/root">
<root>
<xsl:for-each select="*[generate-id() = generate-id(key('firsts',local-name()))]">
<xsl:copy-of select="." />
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
输出:
<?xml version="1.0"?>
<root>
<paymentFlag>true</paymentFlag>
<cardFlag>true</cardFlag>
<pinFlag>true</pinFlag>
<outputFlag>false</outputFlag>
</root>
确定我使用的元素的全名local-name()
,因为大多数初学者不关心 namespace ,并且不考虑:
之前的部分QName如abc:Element
。因此,如果您需要考虑namespace
,请将local-name()
替换为name()
。
答案 1 :(得分:0)
这是一个使用xsl:key
的XSLT 1.0选项,与currently accepted answer非常相似。
不同之处在于,如果您从XSLT 1.0更改为2.0,则此答案不会中断。它适用于两个版本。 (另一个答案将失败,错误&#34; A sequence of more than one item is not allowed as the first argument of generate-id()
&#34;如果以2.0版本运行。)
另一个区别是我使用name()
代替local-name()
。通过这样做,我将具有名称空间前缀的元素视为不同(它们是)。如果您想对它们进行相同处理,请使用local-name()
。 (另外,如果你想处理不同默认命名空间中的元素(没有前缀),你可以使用namespace-uri()
创建一个复合键。如果你想要一个例子,请告诉我。)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="firsts" match="/*/*" use="name()"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:for-each select="*[count(.|key('firsts',name())[1])=1]">
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
如果您已经在使用2.0,那么使用xsl:for-each-group
这是另一个仅限2.0的选项......
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:for-each-group select="*" group-by="name()">
<xsl:apply-templates select="current-group()[1]"/>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
请注意,我在两个选项中都使用了xsl:apply-templates
,以便日后更容易扩展样式表。
答案 2 :(得分:-1)
相同级别,但其中一些是重复的
问题是什么被认为是重复的。如果只检查元素名称,您可以尝试这样的事情:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="root/*">
<xsl:variable name="this" select="." />
<xsl:if test="not(preceding-sibling::*[name() = name($this)])" >
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
答案 3 :(得分:-1)
使用xsl样式表版本2
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />
<xsl:template match="/">
<root>
<xsl:for-each select="root/*[not(name()=preceding::*/name())]">
<xsl:element name="{name()}">
<xsl:value-of select="text()"/>
</xsl:element>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
输出:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<paymentFlag>true</paymentFlag>
<cardFlag>true</cardFlag>
<pinFlag>true</pinFlag>
<outputFlag>false</outputFlag>
</root>