其实 XSLT 能够做的事情很多,绝对超乎你的想象。除了格式转换,XSLT 还能完成一些看起来和格式转换完全无关的工作。比如说文件访问或者是数据库查询等等。而这一切都要归功于 XSLT 扩展(XSLT Extension)。
XSLT 是一种基于规则的格式转换语言。在许多人眼里,它的功能就是将一种格式的 xml 文件转换成另外一种格式的 xml 文件,仅此而已。不过,事实真是这样吗?
其实 XSLT 能够做的事情很多,绝对超乎你的想象。除了格式转换,XSLT 还能完成一些看起来和格式转换完全无关的工作。比如说文件访问或者是数据库查询等等。而这一切都要归功于 XSLT 扩展(XSLT Extension)。
根据 XSLT 1.0 的规范,符合标准的 XSLT 引擎应该支持 XSLT 扩展。也就是允许用户自定义 XSLT 的扩展元素(extension elements)和函数(extension functions)。今天我们所看到的主流 XSLT 引擎都按照国际标准,提供了自己的扩展方式。而开源软件中的 saxaon 和 xalan,在这方面走得更远。
Saxon 和 xalan 都是基于 java 开发的 XSLT 引擎,为它们编写扩展自然也基于 java。一般只要以下 3 步就可以完成一个扩展了。
1. 编写一个 java 类,在这个类里面设计好扩展功能,并以静态方法的形式提供给XSLT 引擎调用。
2. 在 XSLT 文件中,声明一个自定义的命名空间(namespace),该命名空间指出了类的位置
3. 在 XSLT 文件中,在适当的地方,调用扩展即可。
接下来让我们看个具体的例子。
foo_txt.xml 是一个待处理的 XML 文件,其中包含了<filename>和<content>两个元素。现在希望通过 XSLT 处理后,能将 <content> 的内容写入名称为 <filename> 的文件中。
图表 1:foo_txt.xml
<?xml version="1.0"?>
<document>
<filename>foo.txt</filename>
<content>Hello,World!</content>
</document>
由于这里牵涉到针对文件的操作,因此这个任务必须通过功能扩展来完成。让我们对照着前文提到的 3 步法,来看看 saxon 是怎么来做的。
图表 2:foo_txt_saxon.xsl
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:user="java:com.pear.utils.FileUtil">
<xsl:template match="document">
<xsl:value-of select="user:output(string(filename),string(content))"/>
</xsl:template>
</xsl:stylesheet>
图表 2
第一步,应该是提供用户编写的自定义java类。由于篇幅关系,这里不再给出源码,请看本文的参考部分,在那里提供了源码下载。
第二步,在XSLT文件开始,通过"xmlns:user='java:com.pear.utils.FileUtil'"命令,我们定义了一个命名空间。
最后,在处理XML节点的过程中,我们通过"user:output"成功地调用了用户自定义扩展函数。从而在XSLT中实现了文件写入功能。
看了saxon的做法之后,如果依样画葫芦的对xalan也来一遍,那么就太没意思了。幸亏xalan提供了一套更有趣的方法。
先直接看看xalan版本的处理文件吧。
图表 3:foo_txt_xalan.xsl
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xalan="http://xml.apache.org/xalan"
xmlns:user="http://www.mac.home">
<xalan:component prefix="user" functions="output">
<xalan:script lang="javascript">
function output(filename,content)
{
var a=new java.io.PrintWriter(filename);
a.print(content);
a.close();
return "Finished!";
}
</xalan:script>
</xalan:component>
<xsl:template match="document">
<xsl:value-of select="user:output(string(filename),string(content))"/>
</xsl:template>
</xsl:stylesheet>
注意到它和saxon的版本有什么不同吗?对了,很明显,用户自定义的函数直接在处理文件中就实现了。而且是用javascript来完成的。那么好处在哪儿呢?答案很简单,就是开发人员可以抛开java的编译环境,直接设计自己的 XSLT 功能扩展了。
