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

32 篇文章 0 订阅

马晨, 软件开发专家

2006 年 1 月 16 日

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扩展的神秘面纱,至于怎么去发掘它的潜力,就留给富有创造力的读者去完成吧。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值