XSLT中exclude-result-prefixes无法移除元素命名空间问题排查
问题根因与原理解释
- XML命名空间是节点的固有属性,不是临时附加的标记。源XML根节点声明了默认命名空间
xmlns="MOM.Production",其下所有无前缀的子节点(A、A1、A2、A2_ITEM、B、B1等)从解析开始就属于该命名空间,命名空间URI和节点本身绑定,和书写时有没有前缀无关。 exclude-result-prefixes属性的作用范围仅覆盖样式表自身声明的命名空间前缀:它只会阻止处理器把样式表里声明、但输出内容里没实际用到的命名空间冗余写到输出根节点上,完全无法修改、移除从源文档复制来的节点自带的命名空间绑定。只要输出的节点本身归属某命名空间,处理器就必须输出对应命名空间声明保证XML合法性,这个逻辑优先级高于该属性配置。xsl:copy-of是深拷贝逻辑,会原封不动保留被复制节点的所有属性、命名空间绑定、子节点结构,不会做任何修改;而用xsl:element手动创建元素时如果没有指定命名空间,默认生成的是无命名空间节点,自然不会携带源文档的默认命名空间,这就是两类元素输出表现不一致的原因。
解决方案
当前使用的XSLT 1.0版本没有2.0版本新增的copy-namespaces="no"便捷配置,不能直接用xsl:copy-of复制需要移除命名空间的源节点,需要通过自定义模板递归重建无命名空间节点,替换原有深拷贝逻辑。
修正后的完整XSLT如下:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:mom="MOM.Production" exclude-result-prefixes="mom" > <xsl:output encoding="UTF-8" indent="yes" method="xml" version="1.0" /> <xsl:template match="/mom:STATUS"> <Telegram> <Body> <xsl:apply-templates select="mom:A"/> <xsl:apply-templates select="mom:B"/> </Body> </Telegram> </xsl:template> <xsl:template match="mom:A"> <A> <xsl:apply-templates select="*" /> </A> </xsl:template> <xsl:template match="mom:B"> <Options> <xsl:for-each select="mom:B1"> <Option code="{text()}"> <xsl:variable name="n"><xsl:number/></xsl:variable> <xsl:attribute name="family"> <xsl:value-of select="$n - 1"/> </xsl:attribute> </Option> </xsl:for-each> </Options> </xsl:template> <!-- 通用模板:重建无命名空间元素,剥离源节点的命名空间绑定 --> <xsl:template match="mom:*"> <xsl:element name="{local-name()}"> <xsl:copy-of select="@*"/> <xsl:apply-templates select="node()"/> </xsl:element> </xsl:template> </xsl:stylesheet>
逻辑说明
- 将原A节点模板中直接深拷贝的
<xsl:copy-of select="*" />替换为<xsl:apply-templates select="*" />,触发自定义的通用节点处理模板 - 通用模板匹配所有归属
MOM.Production命名空间的源元素,通过local-name()取元素不带命名空间的本地名称,创建无命名空间的同名新元素,从根源上避免携带源命名空间 - 源XML中的属性未绑定命名空间,可直接通过
xsl:copy-of select="@*"复制,不会带出命名空间声明 - 模板递归处理所有子节点,保证深层嵌套的A2、A2_ITEM等节点全部被重建为无命名空间节点
- 样式表中直接写固定名称的无命名空间元素(比如
<Telegram>、<A>)可以直接写标签名,不需要额外用xsl:element包裹,效果完全一致,代码更简洁
用上述样式表转换源XML,输出结果和预期结果完全一致,不会携带任何MOM.Production命名空间声明。
补充:如果后续升级到XSLT 2.0及以上版本,不需要写自定义重建模板,直接在复制A节点子元素的
xsl:copy-of上增加copy-namespaces="no"属性,即可实现复制时不携带源节点命名空间的效果。
内容的提问来源于stack exchange,提问作者Hà Hoàng
相关产品推荐
相关产品推荐

