XSL提供两种元素来让你指派值到变量上:
<xsl:variable>和
<xsl:param>。它们享用共同的命名空间和语法,都使用
$name
来引用变量。这两个元素最主要的不同是
param's
的默认值能够被模板调用的
<xsl:with-param>所取代,就如上面最后一个例子所示。
<xsl:param name="cols">1</xsl:param> <xsl:variable name="segnum" select="position()"/>
在上面两个元素中,
param
和
variable
的名字都是通过
name
属性来指定的,可以看到
param
的名字是
cols
,
variable
的名字是
segnum
。它们的值可以通过两种方式来提供,参数的例子是通过元素的内容值“1”来赋值的,而变量的例子是通过
select
属性值来赋值的,这个属性值是一个表达式的结果,而元素本身并没有内容值。
对于新接触XSL的用户来说变量的特性有点古怪,当你给一个变量赋值后,你就不能在它的应用周期内改变它的值,如果这样做会报错。所以你不能像在使用其它编程语言那样对变量进行动态存储,变量在它的应用周期内持有的是固定值,并在应用周期结束时销毁。这个特性是在设计XSL时就决定了,因为XSL是模板驱动而非流程驱动的。这意味着它没有固定的执行顺序,所以你无法依赖一个能够改变值的变量。要想正确的使用变量,你必须理解变量的周期是如何定义的。
如果一个变量定义在所有模板的外部,那么它就被认为是一个全局变量,它对所有模板都生效。全局变量的值是固定不变的,也不能被任何模板所重新赋值。但是你可以在模板内创建一个与全局变量同名的本地变量,然后赋予其它的值。本地变量只能在其自己的应用周期内起作用。
定义在模板里的本地变量只会在它被允许的周期内生效,也就是对在它之后的同胞和后裔有效。要想理解这个周期,你必须明白XSL指令其实就是纯粹的XML元素,并内嵌在XML家族层级结构中。它们通常是指父级、子级、同级、祖先级和后裔级。在XML家族层级中,给一个变量赋值就像发布一个公告给你希望听到家族成员一样。你只能把公告发布给比你年龄低的同级(包括你自己)和它们的后裔级,也就是说定义在你前面的年长的同级将不会听到公告,更不用说你的父级和祖先了。如果你发布不同的公告内容但是用相同的公告名给相同的被通知成员,那将出现错误,(言外之义,你重新给变量赋值了)。请记住这里的家族并不是你的文档元素,而只是在你
stylesheet
中的XSL指令。手工编写
stylesheet
将对你跟踪周期很有帮助,XSL元素缩进和嵌套将帮助你理解周期。下面的代码片段来自DocBook stylesheet中的
pi.xsl
文件,举例说明两个变量周期的不同。
1 <xsl:template name="dbhtml-attribute"> 2 ... 3 <xsl:choose> 4 <xsl:when test="$count>count($pis)"> 5 <!-- not found --> 6 </xsl:when> 7 <xsl:otherwise> 8 <xsl:variable name="pi"> 9 <xsl:value-of select="$pis[$count]"/> 10 </xsl:variable> 11 <xsl:choose> 12 <xsl:when test="contains($pi,concat($attribute, '='))"> 13 <xsl:variable name="rest" select="substring-after($pi,concat($attribute,'='))"/> 14 <xsl:variable name="quote" select="substring($rest,1,1)"/> 15 <xsl:value-of select="substring-before(substring($rest,2),$quote)"/> 16 </xsl:when> 17 <xsl:otherwise> 18 ... 19 </xsl:otherwise> 20 </xsl:choose> 21 </xsl:otherwise> 22 </xsl:choose> 23 </xsl:template>
变量
pi
的周期开始于第8行,也就是模板定义它的位置,结束于第20行它最后一个同级兄弟结束的地方
[1]。变量
rest
的周期开始于13行,结束与15行。幸运的是,15行的输出表达式赶在周期结束前使用了变量值。
让我们来看看当在变量的周期内使用
<xsl:apply-templates/>会如何?被应用的模板内会得到变量值吗?答案是否定的。因为被应用的模板生效周期并没有真正的在变量周期内,它在
stylesheet
的其它地方退出,并不是在变量的低龄同级和后裔内退出。
要想传值给一个模板,你可以使用
<xsl:with-param/>传递一个参数。这种参数传递通常被用在使用
<xsl:call-templates/>调用指定模板,尽管你也可以使用
<xsl:apply-templates/>调用模板,但是通常被调用的模板希望传入一个与
<xsl:param/>定义同名的参数。这样就可以在模板内使用这个参数值。任何传入的参数名如果在模板内没有被定义将被忽略处理。
<xsl:call-template name="head.content"> <xsl:with-param name="node" select="$doc"/> </xsl:call-template>
上面一个命名为
head.content
的模板被调用,在调用周期内传递了一个名为
node
的参数,参数值是变量
$doc
。上面被调用的模板看上去会是下面的样子:
<xsl:template name="head.content"> <xsl:param name="node" select="."/> ...
模板期望一个参数是因为模板定义中声明了一个
<xsl:param/>,并且名字和传入参数名相同。模板内的
<xsl:param/>提供了一个默认值,如果传入的参数名没有与其匹配,那么将在模板内使用默认值。