XML的姊妹语言——XSLT(可扩展样式表语言转换)可以对XML进行操作,将其转换成任何基于文本的形式。目前,很多开发人员都用XSLT将XML转换成HTML,当然,这只是其中一种用途(见图15-1)。
|
|
图 15-1
XSLT文件称为样式表,由一些模板组成。模板属于XML的某个特性的一部分(使用XPath指定),它可以决定为这一部分输出什么文本。通过为不同的元素和条件定义模板,XSLT样式表变成了一种XML 解析器。例如,考虑前面用过的XML:
现在假设你想将这个雇员列表按照以下的HTML格式显示:
其实,就是从内容中拉出<name/>元素的内容,然后将其放入一个无序列表中。然如要将<employee/>的title特性取出,并放在<em/>元素内的名字旁边,则可以创建一个XSLT样式表:
可以看到,XSLT其实就是另一个基于XML的语言。文档元素是<xsl:stylesheet/>,它还指定了XSLT使用的版本(1.0)以及命名空间URL。缺少这个信息,XSLT处理器则无法正确使用样式表。
下面一行包含<xsl:output/>元素,它指定了输出处理的规则。对于method特性,有三种可能的值:html、xml和text。使用html时,解析器将输出作为HTML对待,也就是说不会应用严格的XML规则;xml强制对输出应用所有的XML规则,同时text只输出包含在元素之外的内容。
下面讨论模板。第一个模板匹配了文档元素,由match= "/".;制定;/ XPath表达式总是指向文档元素。在模板中还有HTML代码,直到<xsl:apply-templates/>元素,让解析器为子节点(这是匹配所有子节点的XPath表达式)应用任何匹配模板。因为已定义了匹配这个模式的模板,所以处理继续进行。
在第二个模板中,注意<li>元素。后面紧跟着<xsl:value-of/>元素,它用于从源XML中输出一个值。select特性是另一个XPath表达式——name,指示输出<name/>元素的值(包含其中的文本)。然后,是一个逗号,然后是起始<em>标签,后面是另一个<xsl:value-of/>元素。这次,select特性指向<employee/>的title特性,这样转换器就输出了title特性值。
XSLT样式表应用于XML文件时,就可以出现前面的HTML结果。尽管例子很简单,不过还是显示了XSLT的一些独特能力。
如果想学习XSLT,请参阅XSLT 2.0: Programmer’s Reference, 3rd Edition(Wiley 出版社ISBN 0-7645-6909-0)。
15.3.1 IE中的XSLT支持
从MSXML 3.0开始,IE就完全支持XSLT 1.0了。如果你还在使用IE 5.0或者5.5,必须手工安装新版的MSXML;如果在使用IE 6.0,那么就表示你至少已经有了MSXML 3.0。
最简单的进行XSLT转换的方法是,分别将XML源代码和XSLT文件载入各自的DOM,并使用特有的transformNode()方法:
这个例子中的一个DOM载入了XML,而另一个DOM载入了XSLT样式表(注意XSLT也可以载入XML DOM因为它也是一种XML格式)。然后,第三行调用文档的transformNode()方法,并将包含XSLT代码的DOM作为唯一的参数传给它。然后在变量sResult中放入转换后的文本结果。
你无需从文档层次开始转换;每一个节点都有个transformNode()方法。下面的均有效:
如果在非文档元素中调用transformNode(),则从这个点开始转换,但是XSLT样式表能访问包含这个节点的整个XML文档。
在IE中使用XSLT的另外一个比较复杂的方法是,使用XSL模板和处理器。这种方法必须使用MSXML的其他几个ActiveX控件。首先,XSLT文件必须载入到一个自由线程DOM文档中(行为和普通DOM一样,但是线程是安全的)。
自由线程DOM文档建立并加载好后,必须将其分配到XSL模板中,这是另一个ActiveX对象:
然后,可以用XSL模板来创建一个XSL处理器(当然也是另一个ActiveX对象):
创建处理器后,将input特性设置为要进行转换的XML DOM节点,然后调用transform()方法:
然后从output特性中访问结果字符串:
这些代码模仿了transformNode()的功能。你肯定在想,如果两种方法都一样,为什么会有人使用XSL模板/处理器的方法。答案是处理器允许你更多地控制XSLT。
例如,XSLT样式表可以接受传入的参数,并将其用作局部变量。考虑下面的样式表:
这个样式表添加了两行代码。第一行是<xsl:param/>元素,它定义了message的参数。第二行通过<xsl:value-of/>元素输出message(美元符号表示这个是局部变量,而不是特性)。
要设置message的值,可在调用transform()前使用addParameter()方法。AddParameter ()方法有两个参数,要设置的参数的名称(与<xsl:param/>中指定的一样)以及分配给它的值(一般是字符串,不过也可以是数字或者是布尔值):
为参数设置一个值后,输出就变成这样了:
可以看到,通过JavaScript传入的值正确地输出了HTML结果。这样,就可以通过加入基于参数的不同行为使样式表更加有弹性。
另一个XSL处理器的高级特性是,设置操作的模式的能力。在XSLT中,可以定义模板的模式。定义mode后,除非<xsl:apply-template />指定按照mode特性进行调用,否则模板不会运行。例如:
这个样式表定义了mode特性为"position-first"的模板(注意可以任意地命名;无任何预定义的模式)。在这个模板中,首先输出雇员的位置,然后输出雇员的名称。为使用这个模板,<xsl:apply-templates/>元素必须也将其mode设置为"position-first"。如果使用这个样式表,它的输出也与前面一样,首先显示雇员名称,然后显示位置。但是,如果用这个样式表,并使用JavaScript将模式设置为"position-first",则先输出雇员的位置:
setStartMode()方法只接受一个参数,要设置的模式。与addParameter()一样,必须在transform()之前调用.
如果要使用同一个样式表进行多次转换,则可在每次转换后重置处理器。调用reset()方法,输入和输出特性则会被清除,处理器又可以使用了。
因为处理器已经编译过XSLT样式表,这要比重复调用transformNode()快得多。
MSXML只支持XSLT 1.0。MSXML的开发自从转到.NET Framework后就停止了。可能,在未来的某天,JavaScript会允许访问XML和XSL的.NET对象。
15.3.2 Mozilla中XSLT支持
从Mozilla 1.2开始,JavaScript开发人员就可用XSLTProcessor对象来进行客户端的XSLT转换了。这个对象使用Mozilla内置的XSLT处理器——Transformiix。
转换过程的第一步是将XML和XSLT载入DOM:
然后,创建XSLTProcessor并使用importStylesheet()方法来分配XSLT DOM:
最后一步是调用transformToDocument()或者transformToFragment(),并以XML DOM为参数,最后返回结果。transformToDocument()返回的是新的DOM文档,transformTo Fragement()返回新的文档碎片,一般来说,应该使用transformToDocument(),除非你想直接将结果添加到某个已经存在的文档中,后一种情况才使用transformToFragement()。
使用transformToFragment()时,仅传入XML DOM并将结果作为另一个完整不同的DOM。
使用transformToFragment()时,传入XML DOM和要添加到结果的文档。这确保了新的文档碎片在目标文档中是有效的。
在前面的例子中,处理器创建了由document对象所有的碎片。这使得碎片添加到了页面中的<div/>元素中。
在XSLT的输出方法是HTML或者XML时,这些当然很有意义,但如果输出的是文本方式呢?要解决这个问题,Mozilla创建了单个元素的XML文档<transformiix:result/>,包含了所有输出的文本。所以,从XSLT文件中使用文本输出也产生有效的文档或者文档碎片。
记住这一点,就可为Mozilla创建transformNode()方法了:
这个方法是用给定的XML DOM创建结果文档。然后使用xml特性(本章前面定义的)将结果XML代码存储到sResult中。然后检查代码是否包含<transformiix:result/>。如果有,则XML部分被去除(只要取出第一个大于号和最后一个小于号之间的文本)。最后,返回sResult。用这个方法,你可以创建在Mozilla和IE中都能使用的代码:
也可以为Mozilla中的XSLTProcessor设置XSLT参数。setParameter()方法接受三个参数:命名空间URI,参数本地名称及要设置的值。一般来说,命名空间URI为null,本地名称就是参数的名称,这个方法必须在transformToDocument()或者transformToFragment()方法之前调用。
还有另外两个方法与参数有关,getParameter()和removeParameter(),分别用于获取某个参数当前的值和删除参数值。它们都以一个命名空间URI(也可以为null)和参数的本地名称为参数:
这些方法不常用,主要是为了方便才提供的。