这是关于 XPath、XML 和 XSLT 的最常见问题解答。搜索“默认命名空间和 XPath 表达式”。
至于解决方案:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:x="http://india.com/states">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="*">
<xsl:element name="{name()}">
<xsl:copy-of select="@*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="*[not(ancestor-or-self::x:city)]">
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
当此转换应用于提供的 XML 文档时:
<country xmlns="http://india.com/states" version="1.0">
<state>
<city>
<name>DELHI</name>
</city>
</state>
</country>
产生想要的结果:
<city>
<name>DELHI</name>
</city>
解释:
在 XPath 中,无前缀的元素名称始终被视为“无命名空间”。但是,所提供的 XML 文档中的每个元素名称都位于非空命名空间(默认命名空间 "http://india.com/states")中。因此,//city 不选择节点(因为 XML 文档中没有任何元素不是命名空间),而 //x:city 其中x: 绑定到命名空间 "http://india.com/states" 选择所有城市元素(位于命名空间"http://india.com/states")。
在此转换中有两个模板。第一个模板匹配任何元素并重新创建它,但在无命名空间中。它还复制所有属性,然后将模板应用到该元素的子节点。
对于不是city 元素的祖先或本身不是city 元素的所有元素,第二个模板覆盖第一个模板。此处的操作是在所有子节点上应用模板。
更新:OP 已修改问题,询问为什么在处理新的、已修改的 XML 文档的结果中有不需要的文本:
<country xmlns="http://india.com/states" version="1.0">
<state>
<city>
<name>DELHI</name>
</city>
</state>
<state2>
<city2>
<name2>MUMBAI</name2>
</city2>
</state2>
</country>
为了不生成文本“MUMBAI”,上面的转换需要稍微修改——忽略(而不是复制)任何没有x:city祖先的文本节点。为此,我们添加以下一行空模板:
<xsl:template match="text()[not(ancestor::x:city)]"/>
现在整个转变变成了:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:x="http://india.com/states">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="*">
<xsl:element name="{name()}">
<xsl:copy-of select="@*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="*[not(ancestor-or-self::x:city)]">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="text()[not(ancestor::x:city)]"/>
</xsl:stylesheet>
结果仍然是想要的,正确的:
<city>
<name>DELHI</name>
</city>