以下是通过名称(在本例中为“removeMe”)从XML文件中删除不需要的元素和属性的工作:
<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="node() | @*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node() | @*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="removeMe"/>
</xsl:stylesheet>
问题是它不区分元素和属性,名称是硬编码的,它只能采用一个名称。 如何重写以使用下面的几个输入参数来删除一个或多个特定元素和/或属性?
<xsl:param name="removeElementsNamed"/>
<xsl:param name="removeAttributesNamed"/>
所需的结果是能够移除 一个或多个 元素和/或 一个或多个 < strong>属性,同时仍然区分元素和属性(换句话说,应该可以删除所有“时间”元素 没有也删除所有“时间” “属性)。
虽然本轮我需要XSLT 1.0,但接受和其他答案的XSLT 2.0解决方案可能对其他人有用。
答案 0 :(得分:22)
此转化:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="removeElementsNamed" select="'x'"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:if test="not(name() = $removeElementsNamed)">
<xsl:call-template name="identity"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
应用于任何XML文档时,请说明:
<t>
<a>
<b/>
<x/>
</a>
<c/>
<x/>
<d/>
</t>
生成所需的正确结果 - 源XML文档的副本,其中任何名称为$removeElementsNamed
参数值的元素的出现都将被删除:< / p>
<t>
<a>
<b/>
</a>
<c/>
<d/>
</t>
请注意: In XSLT 1.0 it is syntactically illegal to have a variable or parameter reference inside a template match pattern 。这就是为什么@JanThomä和@treeMonkey的解决方案都会引起任何符合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:param name="removeElementsNamed" select="'|x|c|'"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:if test=
"not(contains($removeElementsNamed,
concat('|',name(),'|' )
)
)
">
<xsl:call-template name="identity"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
当应用于同一XML文档(上图)时,转换再次生成所需的正确输出 - 源XML文档,其中包含名称在$removeElementsNamed
参数中指定的所有元素 - 已删除强>:
<t>
<a>
<b/>
</a>
<d/>
</t>
Update2 :与 Update1 中的转换相同,但是使用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:strip-space elements="*"/>
<xsl:param name="removeElementsNamed" select="'|x|c|'"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match=
"*[name() = tokenize($removeElementsNamed, '\|')]"/>
</xsl:stylesheet>
更新:OP已添加要求,也可以删除具有某些特定名称的所有属性。
以下是稍微修改过的转换以适应这一新要求:
<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:param name="removeElementsNamed" select="'x'"/>
<xsl:param name="removeAttributesNamed" select="'n'"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:if test="not(name() = $removeElementsNamed)">
<xsl:call-template name="identity"/>
</xsl:if>
</xsl:template>
<xsl:template match="@*">
<xsl:if test="not(name() = $removeAttributesNamed)">
<xsl:call-template name="identity"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
将此转换应用于下面的XML文档(之前使用的那个,但添加了一些属性):
<t>
<a>
<b m="1" n="2"/>
<x/>
</a>
<c/>
<x/>
<d n="3"/>
</t>
生成了需要的正确结果(名为x
的所有元素和名为n
的所有属性都被删除了):
<t>
<a>
<b m="1"/>
</a>
<c/>
<d/>
</t>
UPDATE2 :正如OP再次请求的那样,我们现在实现了传递管道分隔的名称列表的功能,以删除具有这些名称的元素,并分别用管道分隔的名称列表删除具有以下名称的属性:
<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:param name="removeElementsNamed" select="'|c|x|'"/>
<xsl:param name="removeAttributesNamed" select="'|n|p|'"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:if test=
"not(contains($removeElementsNamed,
concat('|', name(), '|')
)
)
">
<xsl:call-template name="identity"/>
</xsl:if>
</xsl:template>
<xsl:template match="@*">
<xsl:if test=
"not(contains($removeAttributesNamed,
concat('|', name(), '|')
)
)
">
<xsl:call-template name="identity"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
将此转换应用于以下XML文档:
<t>
<a p="0">
<b m="1" n="2"/>
<x/>
</a>
<c/>
<x/>
<d n="3"/>
</t>
生成所需的正确结果(名称为c
和x
的元素以及名称为n
和p
的属性将被删除) :
<t>
<a>
<b m="1"/>
</a>
<d/>
</t>
答案 1 :(得分:3)
如果您可以使用2.0,这是一个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:strip-space elements="*"/>
<xsl:param name="removeElementsNamed" select="'bar,baz'"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[name()=tokenize($removeElementsNamed,'[\|, \t]')]"/>
</xsl:stylesheet>
答案 2 :(得分:0)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="removeMe"/>
<xsl:template match="node() | @*">
<xsl:if test="not(name(.)=$removeMe)">
<xsl:copy>
<xsl:apply-templates select="node() | @*"/>
</xsl:copy>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
答案 3 :(得分:-1)
这是一些hacky,但它可能会给你一般的想法:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="removeElementsNamed"/>
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[contains($removeElementsNamed, concat(',',name(),','))]"/>
您需要指定要删除的元素名称作为逗号分隔列表,以逗号开头并以逗号结尾,例如值“,foo,bar,baz”将删除所有名为foo bar或baz的元素。如果您没有任何元素是其他元素的部分名称,您可以将其简化为:
<xsl:template match="*[contains($removeElementsNamed,name())]"/>
但是,如果你有像
这样的XML<foo>
<bar>..<bar>
<barbara>..</barbara>
<foo>
并使用“bar”作为参数,这将删除bar和barbara标签,因此第一种方法更安全。