有一种方法给 XSLT 中变量进行赋值,是保持状态的那种

在 XSLT 中声明变量可以用 <xsl:variable> 和 <xsl:param>,它们的区别是可以通过 <xsl:param> 从外部向 XSLT 文件传参数,除此之外,在 xslt 内部使用时这两者的用法基本是一样的。下面只以 <xsl:variable> 为例子,例子中的 xsl:variable 替换成 xsl:param 也是能 run 的。

<xsl:variable> 的基本用法是:

上面三行同时写在一个 <xsl:template/> 里是没问题的,最后显示出新的值为 'New Value',但是跨了多次模板调用就有问题了,即使是把第一行写在最外层看起来像个全局变量。等会例子会揭示出现像来,先记住一点,在 XSLT 中每次应用模板就像是一次方法调用一样,那好,看个例子吧,有三部分组成,XML、XSLT、XsltTransformer 类,这三个文件都放在 cc/unmi/xslt 包中:

1. test.xml 文件:

2. XsltTransformer.java 文件:

3. test_vars.xml 文件:

执行 XsltTransformer 的输出结果是:

        2, Last username: Initial
     3, Current username: Unmi

        2, Last username: Initial
     3, Current username: Any 

        1, Last username: Initial

分析结果想原因:从 <xsl:apply-templates select="user"/>  到应用模板 <xsl:template match="user">,相当于是一次方法调用,username 像是方法参数,在模板中可以改变 username 的值,但是再次进入时又是初始值,当然在全部遍历完之后,username 还是它的初始值。也就是,尽管 username 声明在最外层,看起来像个全局变量,但是却无法在模板中改变它的值。所以你不能像写通常的 Java 程序那样,设个全局标志,在某个方法中进行赋值,最后作为条件来判断。

看著名的 W3 Schooll 中关于 XSLT <xsl:variable> 元素 的注释:  一旦您设置了变量的值,就无法改变或修改该值!

比如我们有碰到下面应用场景的时候:

显然,通过 <xsl:variable> 是做不到的,因为从模板中出来之后变量又变回去了。

要实现上面的功能,我们需要走别的路子了,试图找到一个真正全局的地方,能保存执行过程中的状态。想啊想啊,我们知道在 XSLT 中可以调用 JS/C#/Java 的方法,最简单的是调用静态方法,那么是否可以调用实例方法呢?如果可行的话,就可以在每次执行 XSLT 时绑定一个 Java 实例(JS/C# 的情况用到时再研究),以此 Java 实例作为数据容器,那么对它其中变量的改变就能够记录下来了。

这种方法确实是可行的,关于 XSLT 中调用 Java 方法的方式有好几种,具体步骤请参考:简单的 Xalan 扩展函数,这里直接用例子说明,如何用 Java 实例来保存 XSLT 所需的变量

这里我们仍然使用前面的 test.xml 和 XsltTransformer.java 文件,但是需要修改 test_vars.xstl 文件,以及创建一个 StatusHolder 来存放 XSLT 中的变量值。

StatusHolder.java:

修改后的 test_vars.xslt 文件:

再次执行 XsltTransformer,控制台输出是:

        2, Last username:
     3, Current username: Unmi

        2, Last username: Unmi
     3, Current username: Any 

        1, Last username: Any

我想这应该是我们所期待的结局。

关键性说明:

1. TransformerFactory.newInstance().newTransformer() 默认使用的就是 Xalan 的转换器
2. 调用 new() 来创建实例的,也可以 new('a', 'b') 来调用相应的有参构造函数
3. 调用实例方法时,第一个参数是实例本身,像调用谢方法一样
4. 可用 <xsl:value-of select="holder:setParam1($statusHolder,'abc')"/> 来调用 setParam1() 方法,尽管该方法的输出为 void。
5. 参数支持 4 种基本类型:数字(Java 双精度)、字符串、布尔和节点集(node-set);如 foo(2) 时,Xalan 倾向于调用方法的顺序是 foo(double)、foo(float)、foo(long) foo(int)、foo(short)、foo(char) 和 foo(byte)。
6. 当被调用方法产生异常时,直接引起 Xalan 的关闭,而且异常栈里有时很难发现问题,所以应该在被调用方法中处理好异常,如有异常时输出信息,不要往外抛。
7. 最后,可以设计一个更好的状态容器,或叫做变量容器,比如用 Map 来保存状态值,而不是一味的罗列 param1 ... paramN。

参考:1. 简单的 Xalan 扩展函数

本文链接 https://yanbin.blog/xslt-assign-variables/, 来自 隔叶黄莺 Yanbin Blog

[版权声明] Creative Commons License 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。

Subscribe
Notify of
guest

2 Comments
Inline Feedbacks
View all comments
吴二文
吴二文
12 years ago

求文章里的英文字体!!不是代码里的。