当前位置: 首页 > 图文教程 > XML家族 > XSLT > 浅谈 XSLT 扩展-让你的 XSLT 如虎添翼

XSLT
XSLT的概念
用XSLT 和XML改进Struts
通过XSLT访问Java代码
XSLT的元素语法
利用XSLT来换XML的根
XSLT:ASP.NET 2.0中XSLT的使用
XSLT:利用 XSLT 对 XML 进行缩进格式化处理
XSLT:七条XHTML代码规范,使你少走弯路

浅谈 XSLT 扩展-让你的 XSLT 如虎添翼


出处:互联网   整理: 软晨网(RuanChen.com)   发布: 2009-08-14   浏览: 414 ::
收藏到网摘: n/a

 

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 功能扩展了。

除了开发语言换成了javascript 外,其它流程和 saxon 的版本还是挺像的。所以就不再详细解释了。

不过值得一提的是,光有javascript还是不够的。在 xalan 版本中,细心的人一定会发现,真正起作用的部分,实际上是一个名字为 PrintWriter 的 java 类。也就是说 javascript 实际上只是一个流程控制者,正是依靠着 java sdk 强大的类库,XSLT 才能如虎添翼,去完成不可能的任务。

那么是不是只有自己写扩展才能解决问题呢?答案当然是否定的。saxon和xalan早就为我们预制了很多公用的扩展功能。我们只要采用拿来主义就可以了。下面我以数据库扩展为例,进一步向你展示XSLT扩展的魅力。

采用 saxon 引擎时,我们引入了几个新的 XSLT 扩展元素,例如 sql:connect,sql:query。通过这些扩展元素,我们可以连接数据库,并执行查询。

比如在下例中,我们可以利用 saxon 提供的 sql 扩展去访问 Informix 数据库。步骤如下:首先我们利用sql:connect建立和数据库的连接,连接使用的参数预先已经定义好了。

其次,我们用sql:query进行查询。查询的字段和查询的条件,均以参数的形式出现。

查询成功之后,利用标准的XSLT元素进行格式解析,并生成HTML格式的表格。

最后,通过sql:close关闭连接。至此整个处理结束。


foo_sql_query.xml
 


<?xml version="1.0"?>
<query>	<table>FOO</table>	<columns>username,birthdate</columns>	<condition/>
</query>
foo_sql_saxon_query.xsl
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:sql="java:/net.sf.saxon.sql.SQLElementFactory"	xmlns:saxon="http://saxon.sf.net/" extension-element-prefixes="saxon sql">
<xsl:param name="driver" select="'com.informix.jdbc.IfxDriver'"/>
<xsl:param name="database"
select="'jdbc:informix-sqli://192.168.0.1:5000/testDB:
INFORMIXSERVER=pcsnet;user=pcs;password=abc'"/>
<xsl:param name="user" select="'pcs'"/>
<xsl:param name="password" select="'abc'"/>
<xsl:template match="/">	<xsl:variable name="connection" as="java:java.sql.Connection"	xmlns:java="http://saxon.sf.net/java-type">	<sql:connect driver="{$driver}" database="{$database}" user="{$user}"	password="{$password}"/>	</xsl:variable> <HTML> <HEAD>	Table of <xsl:value-of select="/query/table"/> </HEAD> <BODY>	<TABLE border="1">	<xsl:variable name="dbtable">	<sql:query connection="$connection" table="{/query/table}"	column="{/query/columns}"/>	</xsl:variable>	<TR>	<xsl:if test="string-length(/query/columns)>0">	<xsl:call-template name="getcolumns">	<xsl:with-param name="columns" select="/query/columns"/>	</xsl:call-template>	</xsl:if>	</TR>	<xsl:apply-templates select="$dbtable/row"/>	<xsl:text>
</xsl:text>	</TABLE>	</BODY>	</HTML>	<sql:close connection="$connection"/>
</xsl:template> <xsl:template name="getcolumns">	<xsl:param name="columns"/>	<xsl:if test="string-length($columns)>0">	<TH>	<xsl:choose>	<xsl:when test="contains($columns,',')">	<xsl:value-of select="substring-before($columns,',')"/>	</xsl:when>	<xsl:otherwise>	<xsl:value-of select="$columns"/>	</xsl:otherwise>	</xsl:choose>	</TH>	<xsl:call-template name="getcolumns">	<xsl:with-param name="columns" select="substring-after($columns,',')"/>	</xsl:call-template>	</xsl:if> </xsl:template> <xsl:template match="row-set">	<xsl:apply-templates select="row"/> </xsl:template> <xsl:template match="row"> <TR> <xsl:apply-templates select="col"/> </TR> </xsl:template> <xsl:template match="col">	<TD> <xsl:value-of select="text()"/>	</TD> </xsl:template>
</xsl:stylesheet>

 

采用xalan引擎时,流程和saxon差不多,不过它还是使用扩展函数来完成数据连接和查询的功能。


foo_sql_xalan_query.xsl
 


<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:sql="org.apache.xalan.lib.sql.XConnection" extension-element-prefixes="sql">
<xsl:param name="driver" select="'com.informix.jdbc.IfxDriver'"/>
<xsl:param name="database" select=
"'jdbc:informix-sqli://192.168.0.1:5000/testDB:INFORMIXSERVER=pcsnet;user=pcs;password=abc'"/>
<xsl:variable name="query">	select <xsl:value-of select="/query/columns"/> from <xsl:value-of select="/query/table"/>
</xsl:variable>
<xsl:template match="/">	<xsl:variable name="connection" select="sql:new($driver,$database)"/> <HTML> <HEAD>	Table of <xsl:value-of select="/query/table"/> </HEAD> <BODY>	<TABLE border="1">	<xsl:variable name="table" select='sql:query($connection, $query)'/>	<TR>	<xsl:for-each select="$table/sql/metadata/column-header">	<TH><xsl:value-of select="@column-label"/></TH>	</xsl:for-each>	</TR>	<xsl:apply-templates select="$table/sql/row-set"/>	<xsl:text>
</xsl:text>	</TABLE>	</BODY>	</HTML>	<xsl:variable name="close" select="sql:close($connection)"/>
</xsl:template> <xsl:template match="row-set">	<xsl:apply-templates select="row"/> </xsl:template> <xsl:template match="row"> <TR> <xsl:apply-templates select="col"/> </TR> </xsl:template> <xsl:template match="col">	<TD> <xsl:value-of select="text()"/>	</TD> </xsl:template>
</xsl:stylesheet>

 

saxon和xalan都是通过jdbc连接数据库的,所以读者如果手头没有informix,只要更换不同的数据库驱动,以及对应的数据库连接参数,就可以在自己的机器上检验效果了。

以上的这些案例只是揭开了XSLT扩展的神秘面纱,至于怎么去发掘它的潜力,就留给富有创造力的读者去完成吧。