XSLT作为用于转换XML文档的语言,本身基于XML语法,因此同样支持命名空间机制。命名空间可以有效区分不同XML词汇表中的同名元素,避免转换过程中出现名称冲突。在XSLT中声明和使用命名空间需要遵循特定的语法规则,下面将从基础概念到实际操作逐步讲解。

XSLT命名空间的基础概念
XML命名空间通过统一资源标识符(URI)来唯一标识一个词汇表,通常配合前缀使用,也可以设置为默认命名空间。XSLT样式表本身需要声明XSLT专用的命名空间,才能正确识别<xsl:stylesheet>、<xsl:template>等XSLT内置元素。
常见的XSLT命名空间URI为http://www.w3.org/1999/XSL/Transform,版本不同URI一致,通过version属性区分XSLT版本,比如1.0、2.0、3.0版本都使用同一个命名空间URI。
XSLT中声明命名空间的方法
1. 声明XSLT内置命名空间
所有XSLT样式表的根元素都需要声明XSLT命名空间,通常使用xsl作为前缀,语法如下:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- 样式表内容 -->
</xsl:stylesheet>
这里的xmlns:xsl就是声明前缀为xsl的命名空间,URI指向W3C定义的XSLT命名空间,这样样式表中所有以xsl:开头的元素都会被识别为XSLT指令。
2. 声明自定义命名空间
如果待转换的XML文档包含自定义命名空间,或者需要在输出结果中使用自定义命名空间,需要在XSLT样式表中同时声明对应的命名空间。比如待转换的XML使用了book前缀的命名空间:
<?xml version="1.0" encoding="UTF-8"?>
<book:library xmlns:book="http://example.org/book">
<book:name>XML入门教程</book:name>
</book:library>
对应的XSLT样式表需要声明相同的命名空间URI,并可以自定义前缀(只要URI一致即可):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:bk="http://example.org/book">
<!-- 样式表内容 -->
</xsl:stylesheet>
这里声明了前缀bk对应URIhttp://example.org/book,和源XML中的book前缀URI一致,后续就可以通过bk前缀访问源文档中的对应节点。
3. 声明默认命名空间
如果不想使用前缀,可以声明默认命名空间,默认命名空间不需要前缀,语法为xmlns="命名空间URI"。比如源XML使用默认命名空间:
<?xml version="1.0" encoding="UTF-8"?>
<library xmlns="http://example.org/book">
<name>XML入门教程</name>
</library>
XSLT样式表中也需要声明相同的默认命名空间,同时如果要使用XSLT指令,还需要保留XSLT的带前缀命名空间:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://example.org/book">
<!-- 样式表内容 -->
</xsl:stylesheet>
注意:XSLT样式表中的默认命名空间会影响其中没有前缀的元素,比如输出的结果元素如果没有前缀,会默认属于这个默认命名空间。
XSLT中使用命名空间的实际场景
1. 匹配带命名空间的源节点
在XPath表达式和模板匹配中,必须使用声明过的前缀来引用带命名空间的节点,不能直接使用本地名称。比如匹配上面自定义前缀的book:name节点:
<xsl:template match="/">
<xsl:value-of select="bk:library/bk:name"/>
</xsl:template>
如果源节点属于默认命名空间,同样需要使用声明的前缀(即使源文档没有前缀)来匹配:
<xsl:template match="/">
<xsl:value-of select="bk:library/bk:name"/>
</xsl:template>
如果忘记加前缀,XPath会找不到对应的节点,导致转换结果为空。
2. 输出带命名空间的结果
如果需要在转换结果中输出带命名空间的元素,只需要在XSLT样式表中声明对应的命名空间,然后在结果元素中使用对应的前缀即可:
<xsl:template match="/bk:library">
<bk:book_info xmlns:bk="http://example.org/book">
<bk:title><xsl:value-of select="bk:name"/></bk:title>
</bk:book_info>
</xsl:template>
也可以在样式表根元素统一声明输出需要的命名空间,这样所有结果元素都可以直接使用前缀,不需要逐个声明。
3. 排除不需要的命名空间
如果源文档的命名空间不需要出现在输出结果中,可以使用exclude-result-prefixes属性排除指定的前缀。比如源文档有bk前缀命名空间,但输出结果不需要这个前缀:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:bk="http://example.org/book"
exclude-result-prefixes="bk">
<xsl:template match="/bk:library">
<book_info>
<title><xsl:value-of select="bk:name"/></title>
</book_info>
</xsl:template>
</xsl:stylesheet>
这样输出的结果中就不会包含bk前缀和对应的命名空间声明。
常见错误和解决方法
- 错误1:匹配节点时忘记加命名空间前缀,导致节点无法匹配。解决方法:检查源文档的命名空间,在XSLT中声明对应的前缀,匹配时使用前缀。
- 错误2:XSLT样式表中声明的命名空间URI和源文档不一致,导致无法识别节点。解决方法:确保两边命名空间的URI完全相同,前缀可以不同。
- 错误3:输出结果中出现了不需要的命名空间声明。解决方法:使用
exclude-result-prefixes属性排除不需要的前缀。
完整示例
下面是一个完整的XSLT转换示例,源XML带自定义命名空间,XSLT正确声明和使用命名空间完成转换:
源XML文件(source.xml):
<?xml version="1.0" encoding="UTF-8"?>
<book:library xmlns:book="http://ipipp.com/book">
<book:name>XSLT实战指南</book:name>
<book:author>张三</book:author>
</book:library>
XSLT样式表(transform.xsl):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:bk="http://ipipp.com/book">
<xsl:output method="xml" encoding="UTF-8"/>
<xsl:template match="/">
<bk:book_result xmlns:bk="http://ipipp.com/book">
<bk:book_title><xsl:value-of select="bk:library/bk:name"/></bk:book_title>
<bk:book_author><xsl:value-of select="bk:library/bk:author"/></bk:book_author>
</bk:book_result>
</xsl:template>
</xsl:stylesheet>
转换后的结果:
<?xml version="1.0" encoding="UTF-8"?>
<bk:book_result xmlns:bk="http://ipipp.com/book">
<bk:book_title>XSLT实战指南</bk:book_title>
<bk:book_author>张三</bk:book_author>
</bk:book_result>