以下是按名称从XML文件中删除不需要的元素和属性(本例中为“removeme”)的操作:
<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"/>
期望的结果是能够移除一个或多个元素和/或一个或多个属性,同时仍然能够区分元素和属性(换句话说,应该可以移除所有“时间”元素而不移除所有“时间”属性)。
虽然这一轮我需要xslt 1.0,但公认的和其他答案中的xslt 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="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
参数的值,都将被删除:<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。这就是为什么@jan thom_和@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文档(如上所述)时,转换将再次生成所需的正确输出—包含在
$removeElementsNamed
参数中指定其名称的所有元素的源XML文档—已删除:<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>