第14章 XSL变换

80酷酷网    80kuku.com

  

XML教程

 

可扩展的样式语言(Extensible Style LanguageXSL)包括变换语言(transformation language)和格式化语言(formatting language)。每种语言都是一个XML应用程序。变换语言提供定义规则的元素如何将XML文档变换成另一个XML文档。被变换的XML文档可能使用原文档的标记和DTD,或者使用一组完全不同的标记。特别是,可能会使用XSL第二部分(格式化对象)定义的标记。本章涉及到XSL变换语言中的部分内容。

本章的主要内容如下:

* 理解XSLXSL变换和模板

* 计算节点的值

* 处理多个元素

* 用表达式选择节点

* 理解缺省的模板规则

* 确定输出要包含的内容

* 复制当前节点

* 对节点进行计数、对输出元素分类以及插入CDATA<符号

* 设置模式特性

* 定义并创建命名模板

* 删除和保留空格

* 基于输入来改变输出

* 合并多个样式单

14.1 何为XSL

变换和格式两部分可相互独立地起作用。例如,变换语言可将XML文档变换成结构整洁的HTML文件,并且完全忽略XSL格式化对象。Internet Explorer 5.0支持这种XSL样式,这在第5章已讨论过,本章着重讨论这种样式。

此外,以XSL格式化对象编写的文档,并非绝对要求在另一个XML文档上使用XSL变换部分才能产生。例如,很容易想象到这样的一个转换器:它是用Java语言写成的,可读取TeXPDF文件,并把这些文件翻译成XSL格式化对象(尽管直到1999年夏天仍没有这样的一种转换器存在)。

实际上,XSL是两种语言,而不是一种。第一种语言是变换语言,第二种是格式化语言。变换语言是一种很有用的语言,它与格式化语言无关。它能够把数据从一种XML表示移到另一种表示,这种功能,使它成为基于XML的电子商务、电子数据交换、元数据交换以及应用于需要在相同数据的不同XML表示之间进行转换的重要组成部分。由于缺乏对人们要浏览的显示器上显示数据的了解,这些用途还要结合起来使用。它们纯粹是用来将数据从一种计算机系统或程序移到另一种计算机系统或程序中。

因此,许多早期的XSL实现都毫无例外地将焦点集中在变换部分,而忽略了格式化对象。这些是不完善的实施方案,但仍然是很有用的。并非所有的数据最终都必须显示在计算机显示器上或打印到纸上。

15"XSL格式化对象"将涉及XSL格式化语言。

有关XSL警告语

XSL仍然处于开发中。XSL语言在过去发生了根本性的变化,将来肯定会再发生变化。本章是根据1999421日的XSL规范草案(第四稿)写成的。读者阅读此书时,此XSL草案可能已经被取代了,精确的XSL句法将会变化。我希望本章与实际的规范不会相差太大。但是,如果的确有不一致的地方,应将本书中的例子与最新规范进行对比。

糟糕的是,仍然没有任何软件能实现1999421日的XSL规范草案(第四稿)的所有内容,甚至不能实现XSL变换的部分。现有的所有产品只能实现当前草案的不同子集。而且,许多产品(包括Internet Explorer 5.0XT)加入的元素并没有出现在当前XSL草案规范中。最后一点是,大多数至少要实现部分XSL内容的产品在其可实现的部分中也存在着很严重的程序错误(bug)。因此,在不同的软件中,只有廖廖无几的几个例子能准确地以相同的方式工作。

当然,随着此项标准向最后版本改进时,当开发商解决了自己产品中的程序错误并实现没有被实现的内容时,以及当出版的更多软件支持XSL时,最终这种情况是可以得到修正的。在达到此目的之前,还得面对这样的选择:要么忍痛使用目前不完善的、未完成的XSL,并且试图避开遇到的所有程序错误和疏忽,要么使用更确定的技术(如CSS),直到XSL更加可靠为止。

14.2 XSL变换概述

XSL变换中,XSL处理程序读取XML文档和XSL样式单。基于处理程序在XSL样式单中找到的指令,输出新的XML文档。

14.2.1 树形结构

就像第6章学到的那样,每个结构整洁的XML文档都是树形结构(tree)。树形结构是一种数据结构,它是由连接起来的节点(node)组成的,这些节点起始于一个称为根节点(root)的单节点。根节点连接它的子节点,每个子节点可以连接零个或多个它自己的子节点,依次类推。没有自己的子节点的节点称为叶节点(leave)。树形结构的图表更像家谱,列出各个先辈的后代。树形结构最有用的特征是每个节点及其子节点也会形成树形结构。因此,一个树形结构就是所有树形结构的分级结构,在此分级结构中,各树形结构都是由更小的树形结构建立的。

XML树形结构的节点就是元素及元素的内容。但是,对于XSL,特性、命名域(namespace)、处理指令以及注释必须也作为节点看待。而且文档的根节点必须与根(基本)元素区别开来。因此,XSL处理程序假定XML树形结构包含下列七类节点:

1.根节点

2.元素

3.文本

4.特性

5.命名域

6.处理指令

7.注释

例如,对于清单14-1中的XML文档,它显示的是元素周期表,在本章我将用这个元素周期表作为例子(更恰当地说,是周期表中的前两个元素)。

完整的元素周期表放在本书所附光盘中的examples/periodic_table文件夹中的allelements.xml文件中。

根节点PERIODIC_TABLE元素包含ATOM子元素。每个ATOM元素含有各种子元素,以便提供原子序数、原子量、符号、沸点等等信息。UNITS特性为具有单位的元素指定单位。

这里使用ELEMENTATOM或许更恰当。但是,写成ELEMENT元素难以区分化学元素和XML元素。因此,起码出于本章的考虑,ATOM似乎更具可读性。

清单14-1:氢和氦元素的XML周期表

<?xml version="1.0"?>

<?xml-stylesheet type="text/xsl" href="14-2.xsl"?>

<PERIODIC TABLE>

<ATOM STATE="GAS">

<NAME>Hydrogen</NAME>

<SYMBOL>H</SYMBOL>

<ATOMIC_NUMBER>l</ATOMIC_NUMBER>

<ATOMIC_WLIGHT>1.00794</ATOMIC_WEIGHT>

<BOILING_POINT UNITS="Kelvin">20.28</BOILING_POINT>

<MELTING_POINT UNITS="Kelvin">13.81</MELTING_POINT>

<DENSITY UNITS="grdMS/cubic centimeter"><!- At 300K ->

0.0899

</DENSITY>

</ATOM>

<ATOM STATE="GAS">

<NAME>Helium</NAME>

<SYMBOL>He</SYMBOL>

<ATOMIC_NUMBER>2</ATOMIC_NUMBER>

<ATOMIC_WEIGHT>4.0026</ATOMIC_WEIGHT>

<BOILING_POINT UNITS="Kelvin">4.216</BOILING_POINT>

<MELTING_POINT UNITS="Kelvin">0.95</MELTING_POINT>

<DENSITY UNITS="grams/cubic centimeter"><!- At 300K ->

0.1785

</DENSITY>

</ATOM>

</PERIODIC_TABLE>

14-1显示的是本文档作为树形结构的图解。它起始于顶端的根节点(不同于根元素!),包括两个子节点:xml-stylesheet处理指令和根元素PERIODIC_TABLE。(XML声明对XSL处理程序是不可见的,因而不包括在XSL处理程序进行操作的树形结构中)。PERIODIC_TABLE元素包括两个子节点,即两个ATOM元素。每个ATOM元素都一个STATE特性的特性节点和各种子元素节点。每个子元素包括其内容的节点,以及任何特性节点和拥有的注释。注意,在特殊情况下,许多节点可以是除元素之外的任何内容。节点可为文本、特性、注释和处理指令。与CSS1不同,XSL不限于只和全部元素一起使用,还有一个更加独特地查看文档的方法:能够根据注释、特性、处理指令等设计样式。

XML声明一样,内部的DTD子集或DOCTYPE声明不是树形结构的一部分。但是,通过使用#FIXED或缺省特性值的<!ATTLIST>声明,它可能具有将特性节点添加到某些元素中的效果。

14-1 以树形图表示的清单14-1

XSL变换语言通过将XML树形结构变换成另一个XML树形结构来操作。这种语言含有操作符,此操作符用来从树形结构中选择特定节点、对节点重新排序以及输出节点。如果有一个节点是元素节点,那么它本身可能就是整个树形结构。请记住,所有的用于输入和输出的操作符都只能操作一个树形结构。它们不是用于变换任意数据的通用的正常表达语言。

14.2.2 XSL 样式单文档

更准确地说,当输入时,XSL变换接受以XML文档表示的树形结构,而输出时,则产生也以XML文档来表示的新的树形结构。因此,XSL变换部分也称为树形结构建立部分。输入和输出的内容必须是XML文档。不能使用XSL来变换成非XML格式(如PDFTeXMicrosoft WordPostScriptMIDI或其他)或从非XML格式进行变换。可使用XSLXML变换为一中间格式(如TeXML),然后使用另外的非XSL软件来将这个中间格式变换成期望的格式。HTMLSGML都是介乎于两者之间的情况,因为它们非常接近于XML。可使用XSL将符合XML的结构完整性规则的HTMLSGML文档变换成XML或者相反。但是,XSL不能处理在大多数Web站点上和文档生成系统中遇到的各种各样非结构整洁的HTMLSGML文档。要牢记的首要问题是,XSL变换语言对于XMLXML的变换是可行的,但对于其他方面则不行。

XSL文档包含一组模板规则和其他规则。模板规则拥有模式(pattern)以及模板(template),模式用来指定模板规则所适用的树形结构,而模板是用来在与此模式匹配时进行输出。当XSL处理程序使用XSL样式单来格式化XML文档时,它对XML文档树形结构进行扫描,依次浏览每个子树形结构。当读完XML文档中的每个树形结构时,处理程序就把它与样式单中每个模板规则的模式进行比较。当处理程序找到与模板规则的模式相匹配的树形结构时,它就输出此规则的模板。这个模板通常包括一些标记、新的数据和从原XML文档的树形结构中复制来的数据。

XSL使用XML来描述这些规则、模板和模式。XSL文档本身也是xsl:stylesheet元素。每个模板规则都是xsl:template元素。规则的模式是xsl:template元素的match特性值。输出模板是xsl:template元素的内容。模板中所有的指令都是由一个或另一个XSL元素来完成的,而这些指令是来完成某种动作,如选择输入树形结构中要包括在输出树形结构的部分。这些由元素名上的xsl:前缀来标识。没有xsl:前缀的元素为结果树部分。

更恰当地说,作为XSL指令的所有元素都是xsl命名域的一部分。有关命名域将在第18"命名域"中讨论。在那以前,只要了解所有的XSL元素的名称都是以xsl:开头就可以了。

清单14-2显示的是一个非常简单的XSL样式单,它有两个模板规则。第一个模板规则与根元素PFRIODIC_TABLE相匹配,它使用html元素来代替此元素。html元素的内容是将文档中的其他模板应用于PFRIODIC_TABLE元素中所获得的结果。

第二个模板与ATOM元素匹配,它用输出文档中的P元素代替输入文档中的每个ATOM元素。xsl:apply-templates规则将匹配的源元素的文本插入到输出文档中。因此,P元素的内容将是包含在相应的ATOM元素中的文本(但不是标记)。下面,将更进一步讨论这些元素的精确语法。

清单14-2:有两个模板规则的周期表XSL样式单

<?xml version="1.0"?>

<xsl:stylesheet

xmlns:xsl="http://www.w3.org/XSL/Transform/1.0">

<xsl:template match="PERIODIC_TABLE">

<html>

<xsl:apply-templates/>

</html>

</xsl:template>

<xsl:template match="ATOM">

<P>

<xsl:apply templates/>

</P>

</xsl:template>

</xsl:stylesheet>

14.2.3 在何处进行XML变换

使用XSL样式单可有三种主要方式将XML文档变换成其他格式(如HTML):

1XML文档和相关的样式单都是用于客户端(Web浏览器)的,然后客户端程序按照样式单中指定的格式变换文档,并将它呈现给用户。

2.服务器将XSL样式单应用于XML文档,以便此文档能够变换成其他某种格式(通常为HTML),并把变换后的文档发送到客户端程序(Web浏览器)。

3.第三个程序将原XML文档变换成其他某种格式(常常为HTML)后,才把此文档放置在服务器上。服务器和客户程序只处理变换后的文档。

这三种方法尽管都使用相同的XML文档和XSL样式,但每一种都使用不同的软件。将XML文档发送到Internet Explorer 5.0的普通Web服务器使用的就是第一种方法。使用IBM alphaWorkXML功能与服务小程序兼容的Web服务器就是第二种方法的例证。使用命令行XT程序来将XML文档变换成HTML文档,然后将HTML文档放置在Web服务器上,采用的就是第三种方法。但是,这些方法都使用(至少在理论上是如此)相同的XSL语言。

本章中,我将重点介绍第三种方法,其主要原因是在撰写本书时,像James ClarkXTIBMLotusXSL这样的专用转换程序能够最完善、最精确地实现目前的XSL规范。此外,该方法提供了与先前的Web浏览器和服务器的最广泛的兼容性,而第一种方法要求浏览器比大多数用户使用的更新;第二种方法要求专门的Web服务器软件。但是,实际上,要求不同的服务器比要求特定客户来得简单。因为可以安装自己的专门服务器软件,但不能要求用户都安装特定的客户软件。

14.2.4 如何使用XT

XTJava 1.1的字符模式的应用程序。要使用它,需要安装与Java 1.1兼容的虚拟机,如SunJava开发包(Java Development KitJDK)或Java的运行时环境(Java Runtime EnvironmentJRE)、AppleMacintosh Runtime for Java 2.1MRJ)或Microsoft的虚拟机。还需要安装符合SAXXML分析程序,如James ClarkXP,这也是一个Java应用程序。

在撰写本书时,可在http://www.jclark.com/xml/xt.html站点上找到XT程序,而在访问http://www.jclark.com/xml/xp/index.html处找到.XP程序。当然,这些URL都随时间可能发生变化。甚至无法担保在你读到此书时XT就能存在。但是,尽管我在本章中使用XT,但使用任何XSL处理程序(执行1999421日制定的XSL规范工作草案的树形结构部分)时,这些实例都能运行。另外的可能性是IBM alphaWorkLotusXSL(可在http://www.a1phaworks.ibm.com/tech/LotusXSL处得到)。当使用执行XSL近期草案标准的软件时,这些例子可能运行,也可能不运行,尽管我希望这些例子更接近于近期标准。我将在我自己的Web站点(http://metalab.unc.edu/xml/books/bible/)上发布任何更新内容。

含有XT main方法的Java类是com.jclark.xsl.sax.Driver。假设JavaCLASSPATH环境变量包括xt.jarsax.jar文件(这两个文件在XT发行版中),那么在命令解释程序的提示符或DOS窗口中键入下面的代码,即可运行XT

C:\>java

-Dcom.jclark.xsl.sax.parser=com.jclark.xml.sax.CommentDriver

com.jclark.xsl.sax.Driver 14-1.xml 14-2.xsl 14-3.html

这一命令行运行java解释程序,将com.jclark.xsl.sax.parser Java的环境变量设置为com.jclark.xml.sax.CommentDriver,后者表示用于解析输入文档的Java类的完整名称。此类必须在类路径中。此处我使用XP语法分析器,但任何符合SAX的语法分析器都可以做到。接下来就是含有XT程序的main()方法的Java类名称(com.jclark.xsl.sax.Driver)。最后,是输入XML文档(14-1.xml)、输入XSL样式单(14-2.xsl)和输出的HTML文件(14-3.html)的名称。如果忽略最后一个参数,那么变换后的文档将打印在控制台上。

如果正在使用Windows,并已安装了Microsoft Java虚拟机,就可以使用XT的单机可执行版。这样,由于它包括XP语法分析器,并且不要求提供CLASSPATH环境变量,所以使用起来就稍微容易一些。对于本程序,可简单地将xt.exe文件放置在自己的路径中,并键入下列句子:

C:\> xt 14-1.xml 14-2.xsl 14-3.html

清单14-2像第6章讨论过的那样将输入文档变换成结构整洁的HTML文件。但是,只要编写的样式单支持这种变换,则可从任何XML应用程序变换到其他应用程序。例如,可以设想有这样的一个样式单,它把VML文档变换到SVG文档:

% java

-Dcom.jclark.xsl.sax.parser=com.jclark.xml.sax.CommentDriver

com.jclark.xsl.sax.Driver pinktriangle.vml

VmlToSVG.xsl -out pinktriangle.svg

当然,尽管其他大多数命令行XSL处理程序具有不同的命令行参数和选项,但它们的表现形式相似。如果这些程序不是用Java来编写,由于不需要配置CLASSPATH,使用起来可能稍微容易些。

清单14-3显示的是通过XT使用清单14-2中的XSL样式单来运行清单14-1时的输出结果。请注意,XT并不简化它所产生的具有许多空白的HTML。但这并不重要,因为最终是在Web浏览器中浏览此文件,而Web浏览器又会将空白截去。图14-2显示的是加载到Netscape Navigator 4.5中的清单14-3。由于清单14-3显示标准的HTML,所以不需要具有XML功能的浏览器来浏览此文档。

清单14-3:将清单14-2中的样式单应用于清单14-1中的XML后产生的HTML

<html>

<P>

Hydrogen

H

1

1.00794

20.28

13.81

0.0899

</P>

<P>

Helium

He

2

4.0026

4.216

0.95

0.1785

</P>

</html>

14-2 将清单14-2中的XSL样式单应用于清单14-1中的XML而生成的页面

14.2.5 直接显示带有XSL样式单的XML文件

无需预处理XML文件,就可以向客户端发送XML文件和描述如何显示此文件的XSL文件。客户程序负责将样式单应用于文档,并按照要求加以显示。这种情况要求客户端所做的工作更多,但服务器的负载要小得多。在这种情况下,XSL样式单必须将文档变换成客户端能够理解的XML应用程序。尽管将来有些浏览器很可能能够处理XSL格式化对象,但HTML是很有希望的选择方案。

XSL样式单与XML文档相链接是很容易的,只需要紧跟在XML声明之后插入序言中的xml-stylesheet处理指令。这种处理指令应有text/xsl值的type特性,以及其值为指向此样式单的URLhref特性。例如:

<?xml version="1.0"?>

<?xml-stylesheet type="text/xsl" href="14-2.xsl"?>

这也是将CSS样式单与文档链接的方法。这里的唯一区别是type特性具有text/xsl值,而不是text/css值。

Internet Explorer 5.0XSL支持在许多方面与1999421日制定的草案有差异。首先,它期望XSL元素放在http://www.w3.org/TR/WD-xsl命名域中,而不是http://www.w3.org/XSL/Transform/1.0命名域,尽管xsl前缀仍然使用。其次,当元素不与任何模板相匹配时,并不执行此元素的缺省规则。因此,在Internet Explorer中浏览文档时,需要从根元素开始为分级结构中的每个元素提供一个模板。清单14-4显示了这种情况。三条规则依次与根节点、根元素PERIODIC_TABLEATOM相匹配。图14-3显示的是使用此样式单将清单14-1加载到Internet Explorer 5.0中之后的XML文档。

清单14-4:将清单14-2调整为可在Internet Explorer 5.0下运行的样式单

<?xml version="1.0"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl">

<xsl:template match="/">

<html>

<xsl:apply-templates/>

</html>

</xsl:template>

<xsl:template match="PERIODIC_TABLE">

<xsl:apply-templates/>

</xsl:template>

<xsl:template match="ATOM">

<P>

<xsl:value-of select="."/>

</P>

</xsl:template>

</xsl:stylesheet>

 

14-3 将清单14-4中调整过的XSL样式单应用于清单14-1中的XML文档, Internet Explorer 5.0中生成的页面

理想的情况是,相同的XML文档既可用于直接显示也可以预处理成HTML。不幸的是,XT不接受http://www.w3.org/TR/WD-xsl命名域,而IE 5不接受http://www.w3.org/XSL/Transform/1.0命名域。 由于不同的处理程序在对改进的XSL规范各部分的支持方面起到各有所长的作用,所以我们仍然处于这种痛苦的境地。

在本章剩下来的部分,在将文档装入浏览器之前,我将把它简单地预处理成HTML格式。

14.3 XSL模板

xsl:template元素定义的模板规则是XSL样式单的最重要的部分。每个模板规则都是一个xsl:template元素。这些规则将特定的输出与特定的输入相关联。每个xsl:template元素都有一个match特性,用来指定要将此模板应用于输入文档的哪个节点。

xsl:template元素的内容是要运用的实际模板。模板可能既包含逐字显示在输出文档中的文本,同时也包含从输入XML文档将数据复制到结果的XSL指令。因为所有的XSL指令都放在xsl命名域中(即它们都是以xsl:开头),所以要区分元素(这些元素就是复制到输出的实际数据)和XSL指令是很容易的。例如,下面为一个应用于输入树形结构根节点的模板:

<xsl:template match="/">

<html>

<head>

</head>

<body>

</body>

</html>

</xsl:template>

XSL处理程序读取此输入文档时,它所看到的第一个节点就是根节点。下面的规则与此根节点相匹配,并告诉XSL处理程序发送此文本:

<html>

<head>

</head>

<body>

</body>

</html>

这种文本就是结构整洁的HTML。由于XSL文档本身就是XML文档,所以其内容(包括模板在内)也必须是结构整洁的XML

如果要在XSL样式单中使用上面的规则,并且只在XSL样式单中使用的话,那么输出就只限于上面的六个标记。(实际上,可压缩为四个等价的标记:<html> <head/> <body/> </html>)。这是由于在规则中没有任何指令告诉格式化程序沿树形结构逐级下移,以便寻找与样式单中的模板进一步的匹配。

14.3.1 xsl:apply-templates元素

要达到根节点以外的地方,就要告诉格式化引擎处理根节点的子节点。一般来说,为了包括子节点中的内容,需递归处理整个XML文档中的节点。可以用来达到此目的的元素就是xsl:apply-templates。把xsl:apply-templates放在输出模板中,就可以告诉格式化程序把与源元素匹配的每个子元素同样式单中的模板相比较。用于匹配节点的模板本身可能包含xsl:apply-templates元素,以便搜索与其子节点的匹配。当格式化引擎处理节点时,此节点是作为整个树形结构来看待的。这是树形结构的优点所在。每个部分都是以处理整体相同的方式来处理。例如,清单14-5就是使用xsl:apply-templates元素来处理子节点的XSL样式单。

清单14-5:递归处理根下子节点的XSL样式单

<?xml version="1.0"?>

<xsl:stylesheet

xmlns:xsl="http://www.w3.org/XSL/Transform/1.0">

<xsl:template match="/">

<html>

<xsl:apply-templates/>

</html>

</xsl:template>

<xsl:template match="PERIODIC_TABLE">

<body>

<xsl:apply-templates/>

</body>

</xsl:template>

<xsl:template match="ATOM">

An Atom

</xsl:template>

</xsl:stylesheet>

当本样式单应用于清单14-1时,将进行以下处理:

1.将根节点与样式单中的所有模板规则进行比较,它与第一个模板规则相匹配。

2.写出<html>标记。

3xsl:apply-templates元素使格式化引擎处理子节点。

A.将根节点的第一个子节点(xml-stylesheet指令)与模板规则相比较,此子节点与任何一个模板规则都不匹配,所以不产生任何输出。

B.将根节点的第二个子节点(根元素PERIODIC_TABLE)与模板规则相比较,此子节点与第二个模板规则相匹配。

C.写出<body>标记。

Dbody元素中的xsl:apply-templates元素使格式化引擎处理PERIODIC_TABLE的子节点。

a.将PERIODIC_TABLE元素的第一个子元素(即氢的ATOM元素)与模板规则进行比较,此子元素与第三个模板规则相匹配。

b.输出文本An Atom

c.将PERIODIC_TABLE元素的第二个子元素(即氦的ATOM元素)与模板规则进行比较,此子元素与第三个模板规则相匹配。

d.输出文本An Atom

E.写出</body>标记。

4.写出</html>标记。

5.处理完成。

最后的结果为:

<html><body>

An Atom

 

An Atom

</body></html>

14.3.2 select特性

为了用ATOM元素的名称(由其NAME子元素给出)来代替An Atom文本,需要指定模板应用于ATOM元素的NAME子元素。为了选择一组特定的子元素,而不是所有的子元素,可向xsl:apply-templates提供select特性,用来指定要选择的子元素。见下面的例子:

<xsl:template match="ATOM">

<xsl:apply-templates select="NAME"/>

</xsl:template>

select特性使用同一类型的模式作为xsl:template元素的match特性。目前,我们坚持使用简单的元素名称;但本章后面有关匹配和选择模式的部分,将讨论selectmatch更多的可能用法,如果不存在select特性,那么选择所有的子元素。

将上面的规则加到清单14-5的样式单,并应用于清单14-5,其结果如下:

<html><head/><body>

Hydrogen

Helium

</body></html>

14.4 使用xsl:value-of来计算节点值

xsl:value-of元素把输入文档中的节点值复制到输出文档中。xsl:value-of 元素的select特性指定正在获取的是哪个节点值。

例如,假设要将文字An Atom代替为由NAME子元素内容给出的ATOM元素的名称,可用<xsl:value-of select="NAME"/>代替An Atom,如下所示:

<xsl:template match="ATOM">

<xsl:value of select="NAME"/>

</xsl:template>

然后,当将样式单应用于清单14-1时,产生如下文本:

<html><head/><body>

Hydrogen

Helium

</body></html>

选择其值的项目(本例中的NAME元素)是与源节点有关的。源节点是由模板来匹配的项目(本例中的特指ATOM元素)。因此,当氢的ATOM<xsl:template match= "ATOM">相匹配,氢的ATOMNAME元素就由xsl:value-of选定了。当氦的ATOM<xsl:template match= "ATOM">相匹配时,氦的ATOMNAME元素就由xsl:value-of选定了。

节点的值总是字符串,有时可能为空字符串。此字符串的精确内容由节点的类型而定。最普通的节点类型为元素,元素节点的值特别简单,就是在元素的开始标记和结束标记之间的所有可析字符数据(但不是标记!)。例如,清单14-1中的第一个ATOM元素如下所示:

<ATOM STATE="GAS">

<NAME>Hydrogen</NAME>

<SYMBOL>H</SYMBOL>

<ATOMIC_NUMBER>l</ATOMIC_NUMBER>

<ATOMIC_WEIGHT>1.00794</ATOMIC_WEIGHT>

<OXIDATION_STATES>1</OXIDATION_STATES>

<BOILING_POINT UNITS="Kelvin">20.28</BOILING_POINT>

<MELTING_POINT UNITS="Kelvin">13.81</MELTING_POINT>

<DENSITY UNITS="grams/cubic centimeter"><!- At 300K ->

0.0899

</DENSITY>

</ATOM>

元素的值显示如下:

Hydrogen

H

1

1.00794

1

20.28

13.81

0.0899

我通过删除所有的标记和注释后计算出了这些值。包括空格在内的其他一切内容都完整无缺地保留着。其他六个节点类型的值也可以类似的非常明显的方式加以计算。表14-1总结了这些值的结果。

14-1 节点值

节点类型

根节点

根元素的值

元素

包括在元素中的所有可析的字符数据(包括元素的任何后代中的字符数据)

文本

节点的文本;实际上为节点本身

特性

标准化的特性值(详细说明见XML 1.0推荐的第3.3.3节);主要为实体还原后的特性值,截去前导和后随的空格;不包括特性名、等号或引号

命名域

用于命名域的URL

处理指令

处理指令的值;不包括<??>以及处理指令名

注释

注释文本,不包括<!---->

14.5 使用xsl:for-each处理多个元素

xsl:value-of元素只用于能够不含糊地确定要获取哪个节点值的上下文中。如果有多个可能项可供选择,那么只选择第一项。例如,由于普通的PERIODIC_TABLE元素包含一个以上的ATOM,所以下列的规则较差:

<xsl:template match="PERIODIC_TABLE">

<xsl:value-of select="ATOM"/>

</xsl:template>

有两种方法可依次处理多个元素。第一种方法已经看到了。只需要按下列方式与select特性(它选择想要包括的特定元素)一起使用xsl:apply-templates

<xsl:template match="PERIODIC_TABLE">

<xsl:apply-templates select="ATOM"/>

</xsl:template>

<xsl:template match="ATOM">

<xsl:value-of select="."/>

</xsl:template>

第二个模板中的select="."告诉格式化程序取匹配的元素(本例中的ATOM)的值。

第二种方法是使用xsl:for-eachxsl:for-each元素依次处理由其select特性选择的每个元素。不过,无需任何附加的模板。例如:

<xsl:template match="PERIODIC_TABLE">

<xsl:for-each select="ATOM">

<xsl:value-of select="."/>

</xsl:for-each>

</xsl:template>

如果省略select特性,那么处理源节点(本例中的PERIODIC_TABLE)的所有子节点。

<xsl:template match="PERIODIC_TABLE">

<xsl:for-each>

<xsl:value-of select="ATOM"/>

</xsl:for-each>

</xsl:template>

14.6 匹配节点的模式

xsl:template元素的match特性支持复杂的语法,允许人们精确地表达想要和不想要与哪个节点匹配。xsl:apply-templatesxsl:value-ofxsl:for-eachxsl:copy-ofxsl:sortselect特性支持功能更加强大的语法的超集,允许人们精确地表达想要和不想要选择哪个节点。下面讨论匹配和选择节点的各种模式。

14.6.1 匹配根节点

为了使输出的文档结构整洁。从XSL变换的第一个输出内容应为输出文档的根元素。因此,XSL样式单一般以应用于根节点的规则开始。要在规则中指定根节点,可将其match特性设置为合适的值。例如:

<xsl:template match="/">

<html>

<xsl:apply-templates/>

</html>

</xsl:template>

本规则应用于根节点,并且只应用于输入树形结构的根节点。当读取到此根节点时,就输出<html>标记,处理根节点的子节点,然后输出</html>标记。本规则推翻了根节点的缺省规则。清单14-6显示了应用于根节点的带有单一规则的样式单。

清单14-6:用于根节点的带有单一规则的XSL样式单

<?xml version="1.0"?>

<xsl:stylesheet

xmlns:xsl="http://www.w3.org/XSL/Transform/1.0">

<xsl:template match="/">

<html>

<head>

<title>Atomic Number vs. Atomic Weight</title>

</head>

<body>

<table>

Atom data will go here

< /table>

</body>

</html >

</xsl:template>

</xsl:stylesheet>

由于本样式单只为根节点提供一条规则,并且由于规则的模板未指明对子节点进行进一步的处理,因而只是按原样输出,所以在模板中所看到的所有内容都将插入到结果文档中。换句话说,将清单14-6中的样式单应用于清单14-1(或其他任何结构整洁的XML文档)中,所获得的结果如下:

<html><head><title>Atomic Number vs. Atomic

Weight</title></head><body><table>

Atom data will go here

</table></body></html>

14.6.2 匹配元素名

正如前面介绍的那样,最基本的模式只包含一个元素名,用来匹配所有带有该名的元素。例如,下面的模板与ATOM元素相匹配,并将ATOM元素的ATOMIC_NUMBER的子元素标成粗体:

<xsl:template match="ATOM">

<b><xsl:value-of select="ATOMIC_NUMBER"/><b>

</xsl:template>

清单14-7显示的是扩充了清单14-6的样式单。首先,在根节点的规则模板中包括了xsl:apply-templates元素。此规则使用select特性来确保只有PERIODIC_TABLE元素获得处理。

其次,使用match="PERIODIC_TABLE"语句创建了只适用于PERIODIC_TABLE元素的规则。本规则设置周期表的标题,然后应用模板来从ATOM元素中生成周期表的主体。

最后,ATOM规则使用<xsl:apply-templates select="NAME"/><xsl:apply-templates select="ATOMIC_NUMBER"/><xsl:apply templates select="ATOMIC_WEIGHT"/>,明确地选择ATOM元素的NAMEATOMIC_NUMBERATOMIC_WEIGHT子元素。它们都包装在HTMLtrtd元素中,以便最终的结果是与原子量相匹配的原子序数表。图14-4显示将清单14-7中的样式单应用于整个周期表文档中的输出结果。

对本样式单需要注意的是:在输入文档中的NAMEATOMIC_NUMBERATOMIC_WEIGHT元素的精确顺序是不重要的。它们在输出文档中以选择它们的顺序出现,也就是说首先为原子序数,然后是原子量。相反,在输入文档中,各个原子依字母顺序排序。以后,将会看到如何使用xsl:sort元素来改变这个顺序,以便使用更常规的原子序数的顺序来排列原子。

清单14-7:利用select的施用于元素的特定类的模板

<?xml version="1.0"?>

<xsl:stylesheet

xmlns:xsl="http://www.w3.org/XSL/Transform/1.0">

<xsl:template match="/">

<html>

<head>

<title>Atomic Number vs. Atomic Weight</title>

</head>

<body>

<xsl:apply-templates select="PERIODIC_TABLE"/>

</body>

</html>

</xsl:template>

<xsl:template match="PERIODIC_TABLE">

<hl>Atomic Number vs. Atomic Weight</hl>

<table>

<th>Element</th>

<th>Atomic Number</th>

<th>Atomic Weight</th>

<xsl:apply-templates select="ATOM"/>

</table>

</xsl:template>

<xsl:template match="ATOM">

<tr>

<td><xsl:value-of select="NAME"/></td>

<td><xsl:value-of select="ATOMIC_NUMBER"/></td>

<td><xsl:value-of select="ATOMIC_WEIGHT"/></td>

</tr>

</xsl:template>

</xsl:stylesheet>

14-4 Netscape Navigator 4.5中显示的原子序数与原子量的关系表

14.6.3 使用/字符匹配子节点

match特性中并不局限于当前节点的子节点,可使用/符号来匹配指定的元素后代。当单独使用/符号时,它表示引用根节点。但是,在两个名称之间使用此符号时,表示第二个是第一个的子代。例如,ATOM/NAME引用NAME元素,NAME元素为ATOM元素的子元素。

xsl:template元素中,这种方法能够用来只与某些给定类型的元素进行匹配。例如,下面的模板规则将ATOM子元素的SYMBOL元素标记为strong。此规则与不是ATOM元素的直系子元素的SYMBOL元素无关。

<xsl:template match="ATOM/SYMBOL">

<strong><xsl:value-of select="."/></strong>

</xsl:template>

请记住,本规则选择的是作为ATOM元素子元素的SYMBOL元素,而不是选择拥有SYMBOL子元素的ATOM元素。换句话说,在<xsl:value-of select="."/>中的.符号引用的是SYMBOL,而不是ATOM

将模式写成一行的形成,就可以指定更深层的匹配。例如,PERIODIC_TABLE / ATOM / NAME选择的是其父为ATOM元素(其父为PERIODIC_TABLE元素)的NAME元素。

还可以使用*通配符来代替层次结构中的任意元素名。例如,下面的模板规则应用于PERIODIC_TABLE孙元素的所有SYMBOL元素。

<xsl:template match="PERIODIC_TABLE/*/SYMBOL">

<strong><xsl:value-of select="."/></strong>

</xsl:template>

最后一点,就如上面所看到的那样,单独的/本身,表示选择文档的根节点。例如,下面的规则应用于文档根元素的所有PERIODIC_TABLE元素。

<xsl:template match="/PERIODIC_TABLE">

<html><xsl:apply templates/></html>

</xsl:template>

虽然 / 引用根节点,但/* 则引用任意根元素。例如,

<xsl:template match="/*">

<html>

<head>

<title>Atomic Number vs. Atomic Weight</title>

</head>

<body>

<xsl:apply-templates/>

</body>

</html>

</xsl:template>

14.6.4 使用//符号匹配子代

有时候,尤其是使用不规则的层次时,更容易的方法就是越过中间节点、只选择给定类型的所有元素而不管这些元素是不是直系子、孙、重孙或其他所有的元素。双斜杠(//)引用任意级别的后代元素。例如,下面的模板规则应用于PERIODIC_TABLE的所有NAME子代,而不管它们具有何种层次的关系:

<xsl:template match=" PERIODIC_TABLE //NAME">

<i><xsl:value-of select="."/></i>

</xsl:template>

周期表实例相当简单,一看就懂,但这种技巧在更深层次,尤其是当元素包含该类的其他元素时(例如ATOM包含ATOM),就显得更加重要。

模式开头的操作符选择根节点的任何子节点。例如,下面的模板规则处理所有的ATOMIC_NUMBER元素,而同时完全忽略其位置:

<xsl:template match="// ATOMIC_NUMBER ">

<i><xsl:value-of select="."/></i>

</xsl:template>

14.6.5 通过ID匹配

有人或许想把一特定的样式应用于特定的单一元素中,而不改变该类型的所有其他元素。在XSL中实现此目的的最简单的方法是,将样式与元素的ID匦韵喙亓??墒褂胕d()选择符(其中包括以单引号括起来的ID值)做到这一点。例如,下面的规则使带有ID值为e47的元素变为粗体:

<xsl:template match="id('e47')">

<b><xsl:value-of select="."/></b>

</xsl:template>

当然,上面假设以此方式选择的元素具有在源文档的DTD中声明为ID类型的特性。但是,通常情况并非如此。首先,许多文档没有DTD,只不过结构整洁,但不合法。即使有DTD,也无法确保任何元素都有ID类型的特性。可以在样式单中使用xsl:key元素,用来把输入文档中的特定特性声明为应该作为ID来看待。

14.6.6 使用来匹配特性

正如第5章已经看到的那样,符号根据特性名与特性相匹配,并选择节点。方法很简单,只需在要选择的特性前加上符号。例如,清单14-8显示一样式单,用它来输出一张原子序数和熔点对照的表格。不仅写出了MELTING_POINT的值,而且也写出了UNITS特性的值。这是由于<xsl :value-of select="UNITS"/>所获得的结果。

清单14-8:使用来选择UNITS特性的XSL样式单

<?xml version="1.0"?>

<xsl:stylesheet

xmlns:xsl="http://www.w3.org/XSL/Transform/1.0">

<xsl:template match="/PERIODIC_TABLE">

<html>

<body>

<hl>Atomic Number vs. Melting Point</hl>

<table>

<th>Element</th>

<th>Atomic Number</th>

<th>Melting Point</th>

<xsl:apply-templates/>

</table>

</body>

</html>

</xsl:template>

<xsl:template match="ATOM">

<tr>

<td><xsl:value-of select="NAME"/></td>

<td><xsl:value-of select="ATOMIC_NUMBER"/></td>

<td><xsl:apply-templates select="MELTING_POINT"/></td>

</tr>

</xsl:template>

<xsl:template match="MELTING_POINT">

<xsl:value-of select="." />

<xsl:value-of select="UNITS"/>

</xsl:template>

</xsl:stylesheet>

回想一下,特性节点的值只是此特性的字符串值。一旦应用清单14-8中的样式单,ATOM元素就会格式化成如下形成:

<tr><td>Hydrogen</td><td>l</td><td>13.8lKelvin</td></tr>

 

<tr><td>Helium</td><td>2</td><td>0.95Kelvin</td></tr>

可以使用各种层次操作符将特性与元素组合起来。例如,BOILING_POINT/UNITS引用BOILING_POINT元素的UNITS特性。ATOM/*/UNITS就能匹配ATOM子元素的任何UNITS元素。当与模板规则中的特性匹配时,这种做法是特别有用的。必须记住,要匹配的是特性节点,而不是包含它的元素。最常见的错误是,不知不觉地将特性节点与包含它的元素节点搞混淆。例如,请看下面的规则,它试图将模板应用于具有UNITS特性的所有子元素:

<xsl:template match="ATOM">

<xsl:apply-templates select="UNITS"/>

</xsl:template>

上面语句实际上做的是,将模板应用于ATOM元素中并不存在的UNITS特性。

也可以使用*来选择元素的所有特性,例如,BOILING_POINT/*可选择BOILING_POINT元素的所有特性。

 

14.6.7 使用comments()来匹配注释

大多数时候,可能应该完全忽略XML文档中的注释。要使注释成为文档的必不可少的部分,确实不是好主意。但是,当不得不选择注释时,XSL确实提供了选择注释的手段。

为了选择注释,可使用comment()模式。尽管此模式有类似函数的圆括号,但实际上决不带任何参数。要区分不同的注释不太容易。例如,回想一下DENSITY元素具有如下的形式:

<DENSITY UNITS="grams/cubic centimeter"><!- At 300K ->

6.51

</DENSITY>

此模板规则不仅输出密度的值和单位,而且还打印测量密度的条件:

<xsl:template match="DENSITY">

<xsl:value-of select="."/>

<xsl:value-of select="UNITS"/>

<xsl:apply-templates select="comment()"/>

</xsl:template>

清单14-1使用注释而不是特性或元素来指定条件,就是为了用于本例。实际应用时,决不要将重要信息放在注释中。XSL允许人们选择注释的唯一真实的理由是,为了用样式单把一种标记语言变换成另一种标记语言,同时又能使注释保持不变。选择注释的任何其他方面的用途都意味着原文档设计得不好。下面的规则匹配所有的注释,并使用xsl:comment元素将它们再次复制出来。

<xsl:template match="comment()">

<xsl:comment><xsl:value-of select="."/></xsl:comment>

</xsl:template>

可是,要注意,用于施加模板的缺省规则对注释无效。因此,遇到注释时,如果要使缺省规则起作用,需要包括xsl:apply-templates元素,无论注释放在何处,此元素都能选择注释。

使用层次操作符可以选择特定的注释。例如,下面的规则匹配DENSITY元素内部的注释:

<xsl:template match="DENSITY/comment()">

<xsl:comment><xsl:value-of select="." /></xsl:comment>

</xsl:template>

14.6.8 使用pi()来匹配处理指令

谈到编写结构化的、智能化的、可维护的XML时,处理指令并不比注释好。但是都有一些必需的应用,其中包括将样式单附加到文档上。

pi()函数选择处理指令。pi()的参数是放在引号内的字符串,表示要选择的处理指令的名称。如果没有参数,则匹配当前节点的第一个处理指令子节点。但是,可以使用层次操作符。例如,下面的规则匹配根节点的第一个处理指令子节点(很可能是xml-stylesheet处理指令)。xsl:pi元素使用指定的名称和输出文档中的值来插入一个处理指令。

<xsl:template match="/pi()">

<xsl:pi name="xml-stylesheet">

type="text/xsl" value="auto.xsl"

</xsl:pi>

</xsl:template/>

下列规则也匹配xml-stylesheet处理指令,但是通过其名称来匹配的:

<xsl:template match="pi( xml-stylesheet )">

<xsl:pi name="xml-stylesheet">

<xsl:value-of select="."/>

</xsl:pi>

</xsl:template/>

事实上,区分根元素和根节点的主要原因之一就是,为了读取和处理序言中的处理指令。尽管xml-stylesheet处理指令使用"名称=值"这样的句法,但XSL并不把它们当做特性看待,这是因为处理指令不是元素。处理指令的值只是跟在其名称后面的空格和结束符?>之间的所有内容。

用来施加模板的缺省规则并不匹配处理指令。因此,遇到xml-stylesheet处理指令时,如果要使缺省规则起作用,需要包括xsl:apply-templates元素,此元素在适当的地方匹配缺省规则。例如,下面这个用于根节点的模板确实将模板应用于处理指令:

<xsl:template match="/">

<xsl:apply-templates select="pi()"/>

<xsl:apply-templates select="*"/>

</xsl:template>

14.6.9 用text()来匹配文本节点

尽管文本节点的值包括在选择的元素值部分中,但它们作为节点通常被忽视。但是,text()操作符确实能够明确选择一个元素的文本子元素。尽管这种操作符有圆括号,但不需要任何参数。例如:

<xsl:template match="SYMBOL">

<xsl:value-of select="text()"/>

</xsl:template>

此操作符存在的主要原因是为了用于缺省规则。无论作者是否指定缺省规则,XSL处理程序必须提供下列的缺省规则:

<xsl:template match="text()">

<xsl:value-of select="."/>

</xsl:template>

这意味着无论何时将模板应用于文本节点,就会输出此节点的文本。如果并不需要这种缺省行为,可以将其推翻。例如,在样式单中,包括下列空模板规则,将会阻止输出文本节点,除非另外的规则明确地匹配。

<xsl:template rnatch="text()">

</xsl:template>

14.6.10 使用"或"操作符|

竖线(|)允许一条模板规则匹配多种模式。如果节点与某种模式相匹配,则此节点将激活该模板。例如,下面模板规则与ATOMIC_NUMBER和ATOMIC_WEIGHT元素都匹配:

<xsl:template match="ATOMIC_NUMBER|ATOMIC_WEIGHT">

<B><xsl:apply-templates/></B>

</xsl:template>

也可以在|两边加入空格,这样使代码更清晰。例如:

<xsl:template match="ATOMIC_NUMBER | ATOMIC_WEIGHT">

<B><xsl:apply-templates/></B>

</xsl:template>

还可以顺次使用两个以上的模式。例如,下面的模板规则作用于A

14.7 选择节点的表达式

xsl:apply-templatesxsl: value-ofxsl:for-eachxsl:copy-ofxsl:sort中,可使用select特性来精确指定对哪个节点进行操作。此特性值即为表达式(expression)。表达式是前节讨论的匹配模式的超集。也就是说,所有的匹配模式都是选择表达式,但并非所有的选择表达式都是匹配模式。让我们来回顾一下,匹配模式能够使用元素名、子元素、后代以及特性来匹配节点,除此之外,还可以对这些项目进行简单的测试。选择表达式可以使用所有的这些条件来选择节点,而且还可以通过参考父元素、同属元素来选择节点,以及通过更加复杂的测试来选择节点。此外,表达式并不局限于只生成一组节点列表,而且还产生布尔值、数值和字符串。

14.7.1 节点轴

表达式并不局限于指定当前节点的子节点和后代节点。XSL提供许多轴(axe),使用这些轴可以从相对于当前节点(通常为模板匹配的节点)的树形结构的不同部分进行选择。表14-2概述了这些轴及其含义。

 

14-2 表达式的轴

选自于

from-ancestors()

当前节点的父节点、当前节点的父节点的父节点、当前节点的父节点的父节点的父节点,依次类推至根节点

from-ancestors-or-self()

当前节点的后代以及当前节点本身

from-attributes()

当前节点的特性

from-children()

当前节点的直系子节点

from-descendants()

当前节点的子节点、当前节点的子节点的子节点,依次类推

from-descendants-or-self()

当前节点本身及其后代节点

from-following()

起始于当前节点末尾之后的所有节点

from-following-siblings()

起始于当前节点末尾之后并且与当前节点具有同一个父节点的所有节点

from-parent()

当前节点的单一父节点

from-preceding()

当前节点开始之前开始的所有节点

from-preceding- siblings()

当前节点开始之前开始的所有节点并且与当前节点具有同一个父节点的所有节点

from-self()

当前节点

from-followingfrom-preceding轴不可靠,可能不会包括在XSL的最终发布版中。如果包括在XSL的最终发布版中,其准确的含义可能会改变。

这些轴的功能是选择表14-2第二列中列出的节点。圆括号中包括要进一步对此节点列表筛选的选择表达式。例如,可能包括由下列模板规则选择的元素名称:

<xsl:template match="ATOM">

<tr>

<td>

<xsl:value-of select="from-children(NAME)"/)

</td>

<td>

<xsl:value-of select="from-children(ATOMIC_NUMBER)"/>

</td>

<td>

<xsl:value-of select="from-children(ATOMIC_WEIGHT)"/>

</td>

</tr>

</xsl:template>

此模板规则匹配ATOM元素。当ATOM元素匹配时,NAME元素、ATOMIC_NUMBER元素和ATOMIC_WEIGHT都从此匹配的ATOM元素的子元素中选择,并作为表的单元格输出。(如果有多个期待的元素

分享到
  • 微信分享
  • 新浪微博
  • QQ好友
  • QQ空间
点击: