XSLT问答:变量与路径
code{font-family:"courier new";color:#006}
pre{font-family:"courier new";background-color:#e1e1e1;color:#006}
a {text-decoration:underline}
XSLT问答:变量与路径
原作:John E. Simpson 2002.06.26
翻译: onestab 2002.08.03
问: 可以改变一个XSLT变量的值吗(Can I change the value of an XSLT variable)?
有没有办法在 xsl:if 或 xsl:when元素内改变一个 xsl:variable元素的值?
答:在XSLT初学者所遇到的困难中,我认为最大的障碍就是这里所用的"变量(variable)"这个词。这是因为XSLT的变量不可以改变,或者确切地说,这个词强调了在运行之前,这个值是不需要知道的:当XSLT处理器为执行转换而处理样式表的时候,它的值每次都可能发生变化。但是,一旦在某个转换中设定了这个值,就不再发生变化。
如果你以前没有用过XSLT的变量,这里先介绍一些背景知识。
样式表中的变量是用xsl:variable元素声明的,例如:
<xsl:varialbe name="empname" select="/employee/name">
这里,将一个由select属性给出的XPath表达式的字符串值赋予一个给定 name的变量。以后就可以引用这个变量的值,方法是在它的名字前加上美元符号($),例如:
<xsl:value-of select="$empname"/>
如果 empname变量的值为 "Munchausen", 则 xsl:value-of 元素就在当前结果树中生成字符串"Munchausen"。
XSLT中最容易引人上当的圈套就是一个变量只在该 xsl:variable的父元素的上下文(context)中才有意义。这就是所谓变量的作用域。你可以在样式表中创建一个xsl:variable作为根元素xsl:stylesheet的子元素,来声明一个全局变量--即在样式表的任何地方都可以访问的变量。举例来说:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="booktitle" select="/book/@title"/>
...
</xsl:stylesheet>
(这就可以避免在样式表中重复出现复杂的XPath表达式,同时又使样式表更容易理解和维护。)
但是 xsl:variable又可以作为XSLT名称空间内几乎所有其它元素的子元素。例如:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="book">
<xsl:variable name="booktitle" select="@title"/>
<xsl:if>
<xsl:when test="substring($booktitle,1,1) <= 'M'">
[书名在字母表中位于前半部分时应用的模板]
</xsl:when>
<xsl:otherwise>
[书名在字母表中位于后半部分时应用的模板]
</xsl:otherwise>
</xsl:if>
</xsl:template>
...
</xsl:stylesheet>
上面模板中,变量booktitle的值会随着给定book元素的title属性的值而变化。不错,它可以变化,但对于任何给定的book元素,它是不变的。
我没看到提问者的代码,他似乎想这么做:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="book">
<xsl:if>
<xsl:when test="substring(@title,1,1) <='M'">
<xsl:variable name="somevar" select="[XPath 表达式]"/>
[书名在字母表中位于前半部分时应用的模板]
</xsl:when>
<xsl:otherwise>
<xsl:variable name="somevar" select="[XPath表达式]"/>
[书名在字母表中位于后半部分时应用的模板]
</xsl:otherwise>
</xsl:if>
</xsl:template>
...
</xsl:stylesheet>
这里有几个潜在的问题。第一个xsl:variable元素的父元素现在是xsl:when元素,因而,该变量不在其它元素,如xsl:otherwise元素中。更重要的是,不管你把xsl:variable放在哪里,它的值不会改变。上例中somevar声明了两次,你实际上创建了两个变量,只不过它们的名字恰好相同,而其作用范围完全不同:一个在xsl:when元素内,另一个在xsl:otherwise内。
我认为要理解XSLT变量的行为,或者为解决你的问题(虽然可能不是你想要的方案),最好的办法是阅读Dave Pawson的XSLT FAQ,特别是关于变量的部分。
问:只用一个DTD能否取得所有可能的文件路径 (How do I extract all possible document paths using just a DTD)?
我试图从一个给定的DTD中取得所有路径:对于每个DTD都存在有一个节点树,对于每个节点都存在一条从根节点到该节点的路径。处理这个问题有没有简单办法?我要取出所有这些路径,并把它们存储到关系数据库中。
答:我猜没有。这个问题没有简单的解决之道。对于一个XML应用,DTD给出了它的有效文档结构的总体图,但是没有对所有可能的有效文档定义出其特定的结构。
假如你有这样一个简单的DTD:
<!ELEMENT root_elem (child_elem+)>
<!ELEMENT child_elem (grandchild_elem*)>
<!ELEMENT grandchild_elem (#PCDATA)>
从这个DTD中只能得出这些信息:目前应用中的一个有效文档有一个根元素root_elem,这个根元素有一个或多个child_elem元素,它(child_elem)可能又有0,1或多个grandchild_elem元素。如果我没理解错的话,在你的提问中这句话有问题--“...对于每个DTD都存在有一个节点树”。实际上不对。对于每个DTD至少存在一个+, *, 以及 ?, 可能的节点树的数量急剧增加,一直到无穷大(如果用了 + 或 * 操作符)。
你提到了RDBMS,是我怀疑你下一步是不是要根据DTD描述的结构建立一个数据库,也就是说,怎样这两者之间建立映射。对这类问题,不能不使人想到Ron Bourret(XML与数据库资料站的开发者),他有篇文章可作为入门。
问:从哪里可以获得基于XML的货币报价?(Where can I get XML-based currency quotes?)
我想在我的站点以CHF(瑞士法郎)发布价格,也想以欧元表示。这样每当页面请求时出现的是实际的报价,但从哪里得到这些报价?
答:关于这类信息的渠道有很多。在我熟悉的当中比较简单的叫做 CEWS(the Currency Exchange Web Service),出自 duzine.com。(注册用户可获得用于开发目的的单个CPU的授权,提供了21种国际货币的报价,包括瑞士法郎、欧元以及美元、英镑、日元、荷兰盾等等。)
CEWS的网页上有个例子演示了CEWS消息的简单结构:
<?xml version="1.0" ?>
<CEWS>
<DATE>21 August 2001</DATE>
<BASE>EUR</BASE>
<CURRENCY_LIST>
<ATS>13.7603</ATS>
<AUD>1.7110</AUD>
<CAD>1.4107</CAD>
<CHF>1.5166</CHF>
...
</CURRENCY_LIST>
</CEWS>
DATE元素代表当前报价的正确日期。BASE元素代表报价的基本货币单位。CURRENCY_LIST元素的子元素代表相应货币名的缩写。在上例中表示奥地利先令、澳元、加拿大元和瑞士法郎,在当天的价格分别为13.7603, 1.7110, 1.4107, 和 1.5166 欧元。
正如CEWS的网页上所指出的那样,这种信息结构非常紧凑,容易处理。它不需要SOAP的包装,也不必通过DTD或XML Schema的验证。
还有许多来源可以提供相似的解决方案,如Evergreen.com,
Oanda, Cloanto。如你所料,如果你想在软件开发用途之外使用这些数据做些别的事情的话,没有几个是免费提供的。你也可以到/n Software的 IPWorks Internet Toolkit 看看,那里支持各种各样的的平台,还有一个货币转换的演示,最后,关于基于SOAP的货币转换,请看XMethods 的 "Euro Conversor"站点
作者介绍:John E Simpson 是Just XML(Prentice Hall PTR;将出第二版)的作者,经常在XML邮件列表上发表文章,特别是在XML-L方面,他有20多年的应用开发经验,目前是佛罗里达州几个非盈利组织的网管。