Wit*_*man 13 xml xslt xslt-2.0 xslt-1.0
以下是通过名称(在此示例中为"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>
Run Code Online (Sandbox Code Playgroud)
问题是它没有区分元素和属性,名称是硬编码的,它只能采用一个名称.如何重写以使用下面的几个输入参数来删除一个或多个特定元素和/或属性?
<xsl:param name="removeElementsNamed"/>
<xsl:param name="removeAttributesNamed"/>
Run Code Online (Sandbox Code Playgroud)
期望的结果是能够移除 一个或多个 元素和/或 一个或多个 属性,同时仍然区分元素和属性(换句话说,应该可以删除所有"时间" 元素 而不删除所有"时间" 属性).
虽然我在本轮中需要XSLT 1.0,但在接受和其他答案中的XSLT 2.0解决方案可能对其他人有用.
Dim*_*hev 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>
Run Code Online (Sandbox Code Playgroud)
当应用于任何XML文档时,请说:
<t>
<a>
<b/>
<x/>
</a>
<c/>
<x/>
<d/>
</t>
Run Code Online (Sandbox Code Playgroud)
生成所需的正确结果 - 源XML文档的副本,其中$removeElementsNamed删除任何名称为参数值的元素的出现:
<t>
<a>
<b/>
</a>
<c/>
<d/>
</t>
Run Code Online (Sandbox Code Playgroud)
请注意:在XSLT 1.0中,在模板匹配模式中包含变量或参数引用在语法上是非法的.这就是为什么@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>
Run Code Online (Sandbox Code Playgroud)
当应用于同一XML文档(上面)时,转换再次生成所需的正确输出 - 源XML文档以及其名称在$removeElementsNamed参数中指定的所有元素- 已删除:
<t>
<a>
<b/>
</a>
<d/>
</t>
Run Code Online (Sandbox Code Playgroud)
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>
Run Code Online (Sandbox Code Playgroud)
更新: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>
Run Code Online (Sandbox Code Playgroud)
当这个转换应用于下面的XML文档时(之前使用的那个,但添加了一些属性):
<t>
<a>
<b m="1" n="2"/>
<x/>
</a>
<c/>
<x/>
<d n="3"/>
</t>
Run Code Online (Sandbox Code Playgroud)
生成所需的正确结果(命名的所有元素x和命名的所有属性n都被删除):
<t>
<a>
<b m="1"/>
</a>
<c/>
<d/>
</t>
Run Code Online (Sandbox Code Playgroud)
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>
Run Code Online (Sandbox Code Playgroud)
将此转换应用于以下XML文档时:
<t>
<a p="0">
<b m="1" n="2"/>
<x/>
</a>
<c/>
<x/>
<d n="3"/>
</t>
Run Code Online (Sandbox Code Playgroud)
的想要的,正确的结果产生(其名称元素c和x并用名字属性n和p被删除):
<t>
<a>
<b m="1"/>
</a>
<d/>
</t>
Run Code Online (Sandbox Code Playgroud)