我试图了解是否有可能使XSLT1.0脚本可与具有相似但不相等的架构共享相同命名空间前缀的xml文件重用。
变量ns-uri
包含所需的命名空间,但似乎 XSLT 1.0 无法识别xmlns:emp='$ns-uri'
中的这种用法,尽管将同一属性与带有命名空间的字符串一起使用是有效的。
我不想遵循使用构造*[name()='emp:department']
的建议,因为这会使 xslt 完全不可读。
还有其他建议,或者这是对 XSLT 1.0 的最终限制吗?
注意:由于我使用的是前缀,因此如何将 XMLNS 属性从 XSL 模板自动传播到另一个模板中的答案不适用。
谢谢。
示例代码
以下方法不起作用:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:param name="ns-uri" select="/child::*[1]/namespace-uri()"/>
<xsl:template match="emp:employee" xmlns:emp="http://www.example.com/ns/employee/2006">
First Name "<xsl:value-of select="emp:first" xmlns:emp="http://www.example.com/ns/employee/2006"/>"
Last Name "<xsl:value-of select="emp:last" xmlns:emp='$ns-uri'/>"
Department "<xsl:value-of select="*[name()='emp:department']"/>"
</xsl:template>
</xsl:stylesheet>
我使用这些 xml 文件作为示例(来自 Finnbarr P. Murphy 的"XSLT 1.0 多命名空间问题"中的示例)
样本1.xml:
<?xml version="1.0"?>
<emp:root xmlns:emp="http://www.example.com/ns/employee/2006">
<emp:employee status="Guru">
<emp:first>John</emp:first>
<emp:last>Kane</emp:last>
<emp:department>IT</emp:department>
<emp:country>IE</emp:country>
</emp:employee>
</emp:root>
样本2.xml:
<?xml version="1.0"?>
<emp:root xmlns:emp="http://www.example.com/ns/employee/2012">
<emp:employee status="Guru">
<emp:first>John</emp:first>
<emp:last>Kane</emp:last>
<emp:department>IT</emp:department>
<emp:country>IE</emp:country>
</emp:employee>
</emp:root>
在这两种情况下,所需的结果应该是:
First Name "John"
Last Name "Kane"
Department "IT"
您看到的问题是由不同的命名空间引起的:
-
sample1.xml将
emp
命名空间定义为"xmlns:emp="http://www.example.com/ns/employee/2006"
-
sample2.xml将
emp
命名空间定义为"xmlns:emp="http://www.example.com/ns/employee/2012"
因此,在一种情况下,您的模板将失败,因为由于命名空间前缀不同(...2006
!=...2012
),其<template...>
规则将不匹配。
因此,要创建一个忽略命名空间的模板,必须忽略命名空间前缀。
这是通过仅关注元素名称的local-name()
而不是整个名称来完成的。
例如,命名空间元素的名称可能emp:first
。
所以name()
会返回emp:first
,而local-name()
只会返回first
.
忽略命名空间前缀(在本例中为emp
)是通过只关注所有相关元素的local-name()
来实现的。
所以下面的模板是由
- 使用
*
选择所有元素 - 并使用谓词
[local-name() = '...']
来限制这组元素,该谓词该谓词检查local-name()
匹配的元素
整个模板如下所示
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="*[local-name() = 'employee']">
First Name "<xsl:value-of select="*[local-name() = 'first']" />"
Last Name "<xsl:value-of select="*[local-name() = 'last']" />
Department "<xsl:value-of select="*[local-name() = 'department']" />"
</xsl:template>
</xsl:stylesheet>
并为两个 XML 输入文件返回相同的结果。
不可以,不能将前缀绑定到变量 URI。
但是,前缀本身没有意义 - 只有 URI 很重要。如果您知道预期的命名空间 URI 是什么,则可以将它们中的每一个绑定到其自己的前缀并同时使用两者,例如:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:emp6="http://www.example.com/ns/employee/2006"
xmlns:emp12="http://www.example.com/ns/employee/2012">
<xsl:output method="text"/>
<xsl:template match="emp6:employee | emp12:employee">
First Name "<xsl:value-of select="emp6:first | emp12:first"/>"
Last Name "<xsl:value-of select="emp6:last | emp12:last" />"
Department "<xsl:value-of select="emp6:department | emp12:department"/>"
</xsl:template>
</xsl:stylesheet>
另一种选择是从传入的 XML 中获取命名空间 URI(就像您开始做的那样),并使用它来生成 XSLT 样式表,其中(常量)前缀绑定到提取的 URI。
然后将生成的样式表应用于原始XML以获得最终结果。