8.8 变量和参数

XSLT允许在样式单文件里定义变量和参数,变量和参数的用法非常相似,区别是变量的值不允许改变,而参数的值可以改变。

XSLT里的变量有点类似于Ant生成文件里的<property…/>元素定义的属性,或者说类似于C语言提供的宏变量,它本质上是一个常量——一旦为该变量指定了值,以后就无法改变。

XSLT里的参数反而更像变量,可以在定义时指定一个参数值,也可以在需要时再改变该参数的值。

8.8.1 为变量和参数指定值

XSLT中使用如下两个元素分别定义变量和参数:

alt <variable…/>:用于定义变量。

alt <param…/>:用于定义参数。

上面这两个元素分别用于定义变量和参数,它们的语法格式除了元素名不同外,其他完全相同。下面是这两个元素的语法格式:

alt

使用<variable…/>和<param…/>元素时可指定如下两个属性:

alt name:用于指定变量或参数的名称。

alt select:可选属性,用于为变量或参数指定值。

实际上,XSLT中为变量和参数指定值有3种情况。

8.8.1.1 指定select属性

select属性的值是一个XPath表达式,XSLT会计算该表达式的值,并将其赋给指定变量或参数。


alt注意

如果在使用<variable…/>和<param…/>元素时指定了select属性,则该元素的元素体必须为空。


访问变量和参数的语法也是完全一样的,在变量名或参数名之前添加$符号即可访问该变量或参数所对应的值。

8.8.1.2 使用元素体指定值

如果在使用<variable…/>或<param…/>元素时没有指定select属性,还可以通过元素体来指定该变量或参数的值,该元素体整体将作为变量或参数的值。

假设有如下XML文件:

程序清单:codes\08\8.8\assignValue.xml

alt

下面的XSLT中使用<variable…/>和<param…/>元素定义了变量和参数:

程序清单:codes\08\8.8\assignValue.xslt

alt

使用上面的XSLT转换前面的XML文档,用浏览器浏览可看到图8.24所示结果。

alt

图8.24 使用变量和参数

需要指出的是,如果需要将元素或变量的值赋给普通元素(非XSLT元素)的属性,则应该使用属性值模板的方式,即采用形如{$varName}或{$paramName}的形式。

8.8.1.3 默认的空值

如果使用<variable…/>或<param…/>元素时既没有指定select属性,又是空元素体,则意味着开发者没有为该变量或参数指定值。系统将为它们分配默认值,默认的变量或参数的值为空字符串。

下列代码定义了一个名为x的变量,该变量的值为空字符串:

alt

上面的代码和如下代码的效果完全相同:

alt

与Ant的生成文件中使用<property…/>定义属性一样,在XSLT中定义变量和参数可以更好地提高代码的可维护性——假设一份XSLT中有多个地方需要使用某个值(例如字符串abcxyz),如果直接在多个地方使用abcxyz字符串,坏处不言而喻:如果有一天需要修改字符串abcxyz,那就得逐个修改每个地方。如果使用变量和参数的形式来处理(例如定义变量a表示abcxyz),那么这份XSLT中重复使用的就是变量a,如果有一天需要修改字符串abcxyz,只需修改变量a的定义即可,这显然提高了代码的可维护性。

8.8.2 全局和局部的变量和参数

根据定义位置的不同,变量和参数可分为如下两种:

alt 全局:直接在XSLT的根元素(<stylesheet…/>或<transform…/>)下定义的变量和参数。

alt 局部:在<template…/>元素下定义的变量和参数。

在<stylesheet…/>或<transform…/>里定义的全局变量和参数可以在整个XSLT样式单里访问,XSLT不要求先定义全局变量和参数然后才能访问,开发者可以在根元素下的任何地方定义全局变量和参数,整个XSLT样式单都可以访问到该变量和参数的值。正如在前面的assignValue.xslt文件中所看到的,即使在XSLT文档最后才定义名为price的参数,而之前已经使用了该price参数,也没有任何问题。

在<template…/>里定义的局部变量和参数只在当前模板内有效。对于局部变量和参数而言,必须遵守前置定义的规则:使用局部变量和参数之前,必须先定义,否则XSLT样式单将出现错误。

例如如下XSLT代码中定义了一个局部的price参数:

程序清单:codes\08\8.8\local.xslt

alt

需要指出的是,同一个<template…/>元素里不能定义2个同名的变量或参数,例如如下代码片段是错误的:

alt

但允许局部变量和参数与全局变量和参数有相同的名字。在这种情况下,局部变量和参数的值将会覆盖全局变量和参数的值。如下所示:

alt

有意思的是,XSLT允许同一份样式单文档里定义多个同名的全局变量和参数。如果在同一份XSLT文档里定义了多个同名的全局变量和参数,那么后面定义的值将会覆盖前面定义的值。如以下代码片段所示:

alt

经过上面两行定义代码之后,price参数的值将变为字符串xxxx,而不是/book/price表达式的值。

8.8.3 改变参数值

前面已经介绍过,XSLT使用<variable…/>元素定义的变量其实是常量,一旦指定了该变量的值,以后就不能改变;而使用<param…/>元素定义的参数则允许改变值。

对于全局参数而言,可以在使用XSLT转换器时从外部传入参数。对于上面的assignValue.xslt样式单文档而言,如果使用Xalan来处理,可以用如下命令为参数传入值:

alt

由此可见,使用Xalan转换器时通过-PARAM选项可为全局参数指定参数值。

如果使用Saxon来处理,可以用如下命令为参数传入值:

alt

由此可见,使用Saxon转换器时直接通过param=value对即可为全局参数指定参数值。

对于在<template…/>元素里定义的局部参数而言,由于它仅在当前<template…/>元素里有效,因此无法从外部为参数指定值。如果开发者需要改变局部参数的值,可以使用<with-param…/>元素。该元素的用法和<param…/>的用法几乎完全一样,能接受的属性也相同。区别在于<param…/>元素的作用是定义一个新的参数,而<with-param…/>元素的作用是改变一个已有参数的值。

实际应用中不会在<template…/>元素内先为某个参数定义一个值,然后又立即改变这个参数的值——这没有多大的实用价值,因此<with-param…/>元素通常和命名模板结合使用。因此<with-param…/>元素只能出现在<apply-templates…/>和<call-template…/>两个父元素之内。

下面的代码示范了<with-param…/>元素的用法:

程序清单:codes\08\8.8with-param.xslt

alt

上面的样式单在①号代码处定义了一个price参数,并指定该参数的值为字符串“预定义的price值”,在②号代码处输出了price参数的值——此处的值并不一定就是字符串“预定义的price值”,这取决于<apply-templates…/>元素是否为price参数重新指定了参数值。实际上该样式单在③号代码处使用<with-param…/>元素为price参数重新指定了值,因此②号代码处输出price参数时将输出字符串“修改后的price值”。