在上一章的内容中,主要介绍了XSLT文档的最重要的构成部分——模板。每个模板都定义了一组处理指令和处理规则,其中,最常用的处理指令莫过于输出指令了。本章将首先介绍输出文档控制指令<xsl:output>,然后着重讲述最常用的输出指令。这些指令包括以下几种。
输出文本——<xsl:value-of>
节点复制——<xsl:copy>、<xsl:copy-of>
生成元素——<xsl:element>
生成属性——<xsl:attribute>
10.1 输出文档控制——<xsl:output>
在前面的内容中提到,XSLT可以将XML文档转换为XML、HTML、TEXT等格式。XSLT究竟将XML源文档转换为哪种格式的文档,正是由<xsl:output>元素决定。
10.1.1 <xsl:output>元素的使用语法
<xsl:output>元素用于控制输出文档的类型及格式。该元素必须作为XSLT文档的顶层元素出现,即只能作为<xsl:stylesheet>元素的子元素出现。其使用语法如下所示。
<xsl:output
method="xml|html|text|name"
version="string"
encoding="string"
omit-xml-declaration="yes|no"
standalone="yes|no"
doctype-public="string"
doctype-system="string"
cdata-section-elements="namelist"
indent="yes|no"
media-type="string"/>
代码说明:
属性method,可选,用于指定输出文档的格式。如果未指定该属性,则默认输出为Xml。另外,如果输出的目标文档中,根元素是<html>,那么也将输出HTML文档。
属性version,可选,用于指定版本信息。此处的version属性要和<xsl:stylesheet>中的version属性区分开来。此处的version代表的不是XSLT文档的版本,而是指输出文档的版本号。如果输出文档为XML文档,那么version属性应该设置为1;如果输出文档为HTML文档,那么version属性应该设置为4。
属性encoding,可选,用于指定输出文档的编码。一般来说选择UTF-8,若目标文档中含有中文字符,则可以选择GB2312。
属性omit-xml-declaration,可选,用于指定输出文档中是否需要忽略XML文档声明语句(也就是<?xml version='1.0'?>语句)。其默认值为no。
属性standalone,可选,用于指定是否要在输出文档中添加独立文档声明。默认值为no。
属性indent,可选。用于指定输出文档是否需要按照结构进行换行和缩进处理。默认值为no。
其余属性,很少用到,在此省略。
<xsl:ouput>元素经常用到的属性包括method、encoding和indent。下面,将结合实例查看这三个属性对于输出文档的影响。
10.1.2 定义输出格式——method属性
method是定义输出格式,它不受输出文件扩展名的影响。例如,即使输出文件名写为xxx.html,那么method="text"的输出内容仍然只有节点的文本。method属性的候选值主要有xml、html和text。清单10-1演示了一个简单的XML文档。
清单10-1 一个简单的XML文档
<?xml version="1.0"?>
<tree>
<branch>first</branch>
<branch>second</branch>
<branch>third</branch>
<branch>fourth</branch>
<branch>fifth</branch>
<branch>sixth</branch>
<branch>seventh</branch>
<branch>eighth</branch>
</tree>
清单10-2演示了一个XSLT文档,该文档用于完全复制清单10-1所示的XML源文档。
清单10-2 复制XML源文档
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<xsl:copy-of select="tree"/>
</xsl:template>
</xsl:stylesheet>
在上面的代码中使用了method="xml",输出文档如下所示。
<?xml version='1.0' ?>
<tree>
<branch>first</branch>
<branch>second</branch>
<branch>third</branch>
<branch>fourth</branch>
<branch>fifth</branch>
<branch>sixth</branch>
<branch>seventh</branch>
<branch>eighth</branch>
</tree>
将清单10-2中的method="xml",修改为method="html",如下所示。
<xsl:output method="html" indent="yes"/>
输出文档如下所示。
<tree>
<branch>first</branch>
<branch>second</branch>
<branch>third</branch>
<branch>fourth</branch>
<branch>fifth</branch>
<branch>sixth</branch>
<branch>seventh</branch>
<branch>eighth</branch>
</tree>
对比method="xml"的输出结果可以知道,method="html"不输出XML文档声明。
将清单10-2的method="xml"修改为method="text",如下所示。
<xsl:output method="text" indent="yes"/>
输出文档如下所示。
first
second
third
fourth
fifth
sixth
seventh
eighth
对比以上两种输出结果可知method="text"只输出节点的文本。
10.1.3 设置输出编码格式——encoding属性
encoding属性的候选值由处理器决定,但是所有的处理器都必须包含UTF-8和UTF-16这两种字符编码。常用的有UTF-8、ISO-8859-1、GB2312、GBK等。由于XML文档是采用Unicode字符集,所以UTF-8是编码范围较广,且经常被采用的编码格式。但这并不是绝对的,正确选择encoding属性的唯一标准是利用该编码格式输出的文档,能被使用该文档的应用程序正确解析。清单10-3演示了一个存储了新闻信息的XML文档。
清单10-3 存储新闻信息的XML文档
<?xml version="1.0"?>
<newspaper>
<news>
<title>三峡大坝近况</title>
<reporter>张心</reporter>
<date>2007.04.15</date>
<keyWord>三峡大坝</keyWord>
</news>
<news>
<title>野生动物保护法普及状况</title>
<reporter>刘军</reporter>
<date>2007.04.15</date>
<keyWord>野生动物</keyWord>
</news>
<news>
<title>亚洲冠军杯</title>
<reporter>王翔</reporter>
<date>2007.04.15</date>
<keyWord>亚洲杯</keyWord>
</news>
<news>
<title>高考来临</title>
<reporter>王翔</reporter>
<date>2007.04.15</date>
<keyWord>高考</keyWord>
</news>
<news>
<title>员工过劳状况调查</title>
<reporter>邹剑</reporter>
<date>2007.04.15</date>
<keyWord>过劳</keyWord>
</news>
<news>
<title>国内导游生存现状</title>
<reporter>王宇</reporter>
<date>2007.04.15</date>
<keyWord>导游</keyWord>
</news>
</newspaper>
现欲将上述XML文档转换为HTML文档,相应的XSLT文档如清单10-4所示。在该XSLT文档中输出编码被设置为UTF-8。
清单10-4 将XML文档转换为HTML文档的XSLT文档
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="UTF-8" indent="yes"/>
<xsl:template match="/newspaper">
<html>
<body>
<table border="1">
<tr align="center">
<td>新闻</td>
<td>记者</td>
<td>日期</td>
<td>关键词</td>
</tr>
<xsl:apply-templates select="news"/>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="news">
<tr align="center">
<xsl:apply-templates select="*"/>
</tr>
</xsl:template>
<xsl:template match="*">
<td>
<xsl:value-of select="."/>
</td>
</xsl:template>
</xsl:stylesheet>
在IE浏览器中查看输出文档,如图10-1所示。这是在浏览器使用UTF-8编码时显示的结果,如果浏览器的编码格式为GB2312时会怎样呢?图10-2演示了修改浏览器的编码格式为GB2312。

图10-1 IE浏览器中查看输出文档 图10-2 修改浏览器的编码格式为GB2312
此时,查看该HTML文档在IE中的显示结果应该如图10-3所示。

图10-3 修改编码格式后的HTML文档
可见,该HTML文档显示为乱码。这是因为转换后的文档与访问该文档的应用程序采用了不同的字符编码。总之,<xsl:output>元素的encoding属性应该参考访问目标文档的应用程序所使用的编码格式,而并非使用哪一种编码就可以一劳永逸地解决问题。
10.1.4 设置自动换行和缩进——indent属性
indent属性用于指定输出文档是否需要根据结构来自动换行和缩进。清单10-5演示了一个关于动物种类的XML文档。
清单10-5 关于动物种类的XML文档
<?xml version="1.0"?>
<animals>
<animal type="carnivorous">lion</animal>
<animal type="carnivorous">wolf</animal>
<animal type="herbivorous">cattle</animal>
<animal type="carnivorous">fox</animal>
<animal type="carnivorous">tiger</animal>
<animal type="herbivorous">horse</animal>
</animals>
现欲筛选出肉食动物,相应的XSLT文档应该如清单10-6所示。
清单10-6 筛选肉食动物的XSLT文档
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="animals">
<xsl:copy>
<xsl:apply-templates select="animal[@type='carnivorous']"/>
</xsl:copy>
</xsl:template>
<xsl:template match="animal">
<xsl:copy>
<xsl:value-of select="."/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
上述XSLT代码中,并未出现<xsl:output>元素,则输出文档默认为XML文档。输出结果如下所示。
<?xml version='1.0' ?>
<animals><animal>lion</animal><animal>wolf</animal><animal>fox</animal><animal>tiger</animal></animals>
在该输出文档中所有的元素都处于一行中,这是因为没有设定<xsl:output>元素的indent属性。显式设定indent属性的代码如下所示。
<xsl:output indent="yes"/>
将该上述代码添加到XSLT文档中,重新查看输出,结果如下所示。
<?xml version='1.0' ?>
<animals>
<animal>lion</animal>
<animal>wolf</animal>
<animal>fox</animal>
<animal>tiger</animal>
</animals>
10.1.5 输出文档控制小结
对于<xsl:output>元素的method属性,要注意其只指定输出文档的格式,这跟输出的文档是什么文档(由输出文档的后缀决定)不能混为一谈。
对于<xsl:output>元素的encoding属性,要注意UTF-8编码格式并非万能。有许多国家和地区的语言编码并未包括在UTF-8中。
<xsl:output>元素的indent属性,这是一个比较容易被忽视的属性。如果未将该属性显式设为真,输出的XML文档结果树(indent只对元素的换行和缩进起作用)在结构上仍然没有问题。但是在对输出文档进行文本切割等操作(例如,使用Linux Shell中的awk对其进行文本分析)时,将极大影响输出结果。
10.2 输出文本——<xsl:value-of>
输出控制指令——<xsl:output>用来控制输出文档的格式,在具体的输出工作中,最常用的则是输出文本。输出文本所用到的指令为<xsl:value-of>。
10.2.1 <xsl:value-of>语法
<xsl:value-of>的使用语法如下所示。
<xsl:value-of select="xpathExpression" disable-output-escaping="yes|no"/>
代码说明:
select属性,必选。指定一个XPath表达式,注意XPath表达式有4种数据类型。
disable-output-escaping属性,可选。用于指定是否禁用输出字符转义。默认值为no。
★ 注意 ★
disable-output-escaping(禁用输出转义)从字面上理解比较拗口。字符转义是指对于一些特殊字符,在输出时要使用实体代替,例如“<”,在输出时转换为“<”。<xsl:value-of>中disable-output-escaping属性的默认值为no,即需要进行字符转义。目前,并非所有处理器都能完美地支持该属性。
下面的代码将输出“<”而不是“<”。
XML源文档如下所示。
<?xml version="1.0"?>
<root>
<![CDATA[<p>contents</p>]]>
</root>
XSLT文档如下所示。
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="/">
<xsl:value-of select="root" disable-output-escaping="yes"/>
</xsl:template>
</xsl:stylesheet>
输出结果如下所示。
<p>contents</p>
10.2.2 典型实例1——输出复杂元素
<xsl:value-of>元素一个主要的应用就是输出元素的文本内容。如果要输出的元素是一个复杂元素,该复杂元素含有多个子元素,那么,将输出所有子节点的文本内容。清单10-7演示了一条关于消息的XML文档。
清单10-7 一个关于消息的XML文档
<?xml version="1.0"?>
<message id="001">
<from>cisco</from>
<to>ANSI</to>
<title>The router article</title>
<body>Three thousands of routers should be sent to Sweden</body>
<description>Cisco router</description>
<dateTime>2005-12-01T00:12:09.001Z</dateTime>
</message>
要想输出节点message的值,相应的XSLT文档应如清单10-8所示。
清单10-8 输出message节点
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:value-of select="message"/>
</xsl:template>
</xsl:stylesheet>
输出结果如下所示。
cisco
ANSI
The router article
Three thousands of routers should be sent to Sweden
Cisco router
2005-12-01T00:12:09.001Z
★ 注意 ★
元素<message>的id属性并未被输出,因为属性节点并不是其所依附的元素节点的子节点。
10.2.3 典型实例2——输出节点集合
元素<xsl:value-of>的属性select用于指定输出的XPath表达式。如果该XPath表达式是一个含多个节点的节点集合,那么将只输出第一个节点的内容。清单10-9演示了含有多个消息的XML文档。
清单10-9 含有多个消息的XML文档
<?xml version="1.0"?>
<messages>
<message id="001">
<from>Cisco</from>
<to>ANSI</to>
<title>The router article</title>
<body>Three thousands of routers should be sent to Sweden</body>
<description>Cisco router</description>
<dateTime>2005-12-01T00:12:09.001Z</dateTime>
</message>
<message id="002">
<from>MS</from>
<to>MySky</to>
<title>The resolution of problem</title>
<body>Some mistakes have been resolved</body>
<description>resolution</description>
<dateTime>2005-12-01T00:12:09.002Z</dateTime>
</message>
<message id="003">
<from>Carrefour</from>
<to>UPS</to>
<title>Required</title>
<body>Three thousands of TV set should be sent to China</body>
<description>TV set</description>
<dateTime>2005-12-01T00:12:09.003Z</dateTime>
</message>
</messages>
用来输出消息的XSLT文档,如清单10-10所示。
清单10-10 输出message节点
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="messages">
<xsl:value-of select="message"/>
</xsl:template>
</xsl:stylesheet>
代码说明:<xsl:value-of select="message"/>用于输出上下文节点的message节点集合。
其输出结果如下所示。
cisco
ANSI
The router article
Three thousands of routers should be sent to Sweden
Cisco router
2005-12-01T00:12:09.001Z
可见message节点集合含有三个<message>元素节点,但是只输出了第一个<message>元素。
10.3 节点复制——<xsl:copy>、<xsl:copy-of>
节点复制是指将XML源文档中的节点直接复制,并输出到目标文档。节点复制主要有两种方式,<xsl:copy>和<xsl:copy-of>。
10.3.1 <xsl:copy>元素的使用
<xsl:copy>元素用来复制上下文节点。其语法格式如下所示。
<xsl:copy use-attribute-sets = Qnames>
<!--instruction and handling-- >
</xsl:copy>
代码说明:
<xsl:copy>用来复制当前节点。该元素实际上只复制节点的开始标签和结束标签。
属性use-attribute-sets,可选。该属性定义一个空白符分隔的属性集列表,凡是在该列表中出现的属性集都将被输出到目标文档中。当然,此时被复制的上下文节点必须为一个元素节点。要使用该属性,必须在XSLT文档中预先定义属性集。
清单10-11演示了一个关于颜色的XML文档。
清单10-11 一个含有5种颜色的XML文档
<?xml version="1.0"?>
<items total="5">
<item>red</item>
<item>green</item>
<item>blue</item>
<item>orange</item>
<item>brown</item>
</items>
除了使用上一章提到递归复制之外,对于一个不太复杂的XML源文档,可以用如清单10-12所示的方式来复制整个XML文档。
清单10-12 复制整个XML源文档
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:template match="/items">
<xsl:copy>
<xsl:apply-templates select="item"/>
</xsl:copy>
</xsl:template>
<xsl:template match="item">
<xsl:copy>
<xsl:value-of select="."/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
转换结果如下所示。
<?xml version='1.0' ?>
<items>
<item>red</item>
<item>green</item>
<item>blue</item>
<item>orange</item>
<item>brown</item>
</items>
代码说明:节点items的模板中,<xsl:copy>只复制了节点items,但是在源文档中还有一个属性total并未被复制。
可以把<xsl:copy>看做生成开始标签<items>;把</xsl:copy>看做生成结束标签<items>。
★ 注意 ★
● <xsl:copy>元素并没有属性来指定复制哪个节点,它只复制上下文节点。
10.3.2 <xsl:copy-of>元素
<xsl:copy-of>元素用来完全复制某个节点。包括该节点的子元素、属性、文本内容。其使用语法如下所示。
<xsl:copy-of select="xpathExpression"/>
代码说明:
属性select,可选。用来指定要复制的节点。
使用<xsl:copy-of>元素来复制清单10-11所示的XML文档,相应的XSLT文档如清单10-13所示。
清单10-13 使用<xsl:copy-of>元素
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:template match="/items">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
代码说明:<xsl:copy-of select="."/>用来复制上下文节点items。注意这里的上下文节点是通过select="."来指定的,而<xsl:copy>元素复制上下文节点则是按固有的语法规则。
输出结果如下所示。
<?xml version="1.0"?>
<items total="5">
<item>red</item>
<item>green</item>
<item>blue</item>
<item>orange</item>
<item>brown</item>
</items>
观察输出结果,可知该语句将整个XML文档重新复制了一份。这是因为被复制的节点是整个XML文档的根元素。
★ 注意 ★
注意区分<xsl:copy>和<xsl:copy-of select="xpathExpression">。<xsl:copy>只可用于复制上下文节点,且只复制节点的开始标签和结束标签;<xsl:copy-of>可以通过select属性指定复制哪个节点,且复制为完全复制。
10.4 生成元素——<xsl:element>
元素是一个XML文档构成的基本单位。在XSLT转换文档中,可以使用指令<elementName>直接生成元素,这样的元素将被直接输出到目标XML文档中。但有时这种方式会失效,例如,元素名需要动态生成、特殊名称空间元素等情况下。这就需要用到生成元素指令<xsl:element>。
10.4.1 <xsl:element>语法
<xsl:element>用于动态生成元素,其使用语法如下所示。
<xsl:element>
name = "element-name"
namespace = "uri-reference"
use-attribute-sets = QName
</xsl:element>
代码说明:
属性name,必选。用来指定欲创建元素的名字。
属性namespace,可选。用来指定被创建元素的名称空间。
属性use-attribute-sets,可选。该属性定义一个被空白符分隔的属性集列表,凡是在该列表中出现的属性集都将被输出到目标文档中。要使用该属性,必须在XSLT文档中预先定义属性集。
10.4.2 典型实例1——动态生成元素名
动态生成元素名,往往由于需要生成的元素名无法直接得到。清单10-14所示的XML文档存储了一些标签信息。
清单10-14 一些公司的简单资料
<?xml version="1.0"?>
<tags>
<tag id="001">
<name>html</name>
<parent>000</parent>
<content/>
</tag>
<tag id="002">
<name>head</name>
<parent>001</parent>
<content/>
</tag>
<tag id="003">
<name>title</name>
<parent>002</parent>
<content>The example of create elements</content>
</tag>
<tag id="004">
<name>body</name>
<parent>001</parent>
<content>How to create a element</content>
</tag>
<tag id="005">
<name>table</name>
<parent>004</parent>
<content/>
</tag>
<tag id="006">
<name>tr</name>
<parent>005</parent>
<content/>
</tag>
<tag id="007">
<name>td</name>
<parent>006</parent>
<content>first</content>
</tag>
<tag id="008">
<name>td</name>
<parent>006</parent>
<content>second</content>
</tag>
</tags>
清单10-14所示的XML文档中,每个元素<tag>都有一个子元素<parent>和id属性。这二者存储了各个<tag>节点之间的关系。现在需要按照这些关联信息重新构建一个XML文档,那么相应的XSLT文档应该如清单10-15所示。
清单10-15 利用关联信息重新构建XML文档
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" method="xml"/>
<xsl:template match="tags">
<xsl:apply-templates select="tag[parent='000']"/>
</xsl:template>
<xsl:template match="tag">
<xsl:element name="{name}">
<xsl:if test="string-length(content) > 0">
<xsl:value-of select="content"/>
</xsl:if>
<xsl:apply-templates select="../tag[parent=current()/@id]"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
代码说明:
<xsl:element name="{name}">用来创建一个新的元素,该元素的名字为上下文节点的子元素<name>的值。因为元素<xsl:element>的属性name的值是一个纯字符串,所以要想使用XPath表达式,只能使用{name}。
<xsl:if test="string-length(content) > 0">用来判断元素<content>的长度是否大于0。如果长度大于0,则表示有需要输出的内容。
<xsl:apply-templates select="../tag[parent=current()/@id]"/>用于重新调用节点tag的模板。不过,select属性所指定的节点集合是那些<parent>子元素的值等于当前节点id属性的tag节点。
查看输出,结果如下所示。
<?xml version='1.0' ?>
<html>
<head>
<title>The example of create elements</title>
</head>
<body>How to create a element
<table>
<tr>
<td>first</td>
<td>second</td>
</tr>
</table>
</body>
</html>
★ 说明 ★
上例说明了<xsl: element>在元素名不确定的情况下使用的情形。
10.4.3 典型实例2——特殊名称空间元素
必须使用<xsl:element>属性的另外一个场合就是需要输出特殊名称空间元素,例如,以“xsl”为前缀的元素。清单10-16所示的XML文档存储了一些简单的指令。
清单10-16 存储一些简单的指令的XML文档
<?xml version="1.0"?>
<instructions>
<instruction>copy</instruction>
<instruction>output</instruction>
<instruction>value-of</instruction>
<instruction>template</instruction>
<instruction>copy-of</instruction>
</instructions>
清单10-16所示的XML文档是一个处理指令集合,现在需要为这些元素添加前缀“xsl”,那么相应的XSLT文档如清单10-17所示。
清单10-17 为元素添加前缀“xsl”
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="instructions">
<xsl:element name="xsl:stylesheet">
<xsl:apply-templates select="*"/>
</xsl:element>
</xsl:template>
<xsl:template match="*">
<xsl:element name="xsl:{.}"/>
</xsl:template>
</xsl:stylesheet>
代码说明:
<xsl:element name="xsl:stylesheet">用于生成一个元素,元素名称为xsl: stylesheet。此处不能直接使用<xsl:stylesheet>,因为处理器会认为这是一条处理指令。
<xsl:element name="xsl:{.}"/>用于为所有元素节点生成一个新的元素。元素的名称为前缀“xsl:”加上下文节点的文本内容。
转换结果如下所示。
<?xml version='1.0' ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:copy/>
<xsl:output/>
<xsl:value-of/>
<xsl:template/>
<xsl:copy-of/>
</xsl:stylesheet>
10.4.4 生成元素小结
本节通过两个典型实例分析了什么时候应该使用<xsl:element>来生成新的元素。当然,使用<xsl:element>场合绝不仅限于此。但是,如果能够使用指令<elementName>来生成元素,那么应当积极使用这种方式。因为相对来说,这种方式比<xsl:element>的执行效率要高。
10.5 生成属性——<xsl:attribute>
在上一节的内容中介绍了生成元素的指令<xsl:element>。相应地,<xsl:attribute>指令用于创建一个属性。由于属性总是依附于元素存在的,所以,该指令总是存在于某个元素的内容模板之内。该指令生成的属性将被添加到该元素中。
10.5.1 <xsl:attribute>语法
<xsl:attribute>元素用于创建属性,并将属性添加到元素节点中。其使用语法如下所示。
<xsl:attribute name="attributename" namespace="uri">
<!-- Content:template -->
</xsl:attribute>
代码说明:
name属性,必选。指定要创建的属性的名称。
namespace属性,可选。为所创建的属性指定名称空间。
<xsl:attribute>元素的内容模板用来确定属性的值。
10.5.2 典型实例1——动态生成属性
与<xsl:element>的情形相同。有时需要生成属性,但是属性名字很难确定。这就无法使用如下所示的代码来直接生成属性。
<element attribute="attributeValue">elementValue</element>
如清单10-18所示的XML文档存储了5条简单的消息。
清单10-18 存储消息的XML文档
<?xml version="1.0"?>
<messages>
<message>
<id>001</id>
<from>Ann</from>
<to>Mike</to>
<title>Required Help</title>
</message>
<message>
<id>002</id>
<from>Bob</from>
<to>John</to>
<title>Study together</title>
</message>
<message>
<id>003</id>
<from>Stephen</from>
<to>Mark</to>
<title>Go Home</title>
</message>
<message>
<id>004</id>
<from>Joe</from>
<to>Alex</to>
<title>Discovery an Island </title>
</message>
<message>
<id>005</id>
<from>Kevvin</from>
<to>Kin</to>
<title>Join us</title>
</message>
</messages>
在清单10-18所示的XML文档中,存储了5条简单的消息。每条消息都有子元素<id>代表消息的序列号、子元素<from>代表消息的发送方、子元素<to>代表消息的接收方、子元素<title>代表消息的标题。现在欲将<messages>元素添加一个属性total用来存储消息的总条数,并将元素<message>的所有子元素以属性的形式表现出来。那么相应的XSLT文档如清单10-19所示。
清单10-19 为清单10-18添加属性
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="messages">
<messages total="{count(message)}">
<xsl:apply-templates select="message"/>
</messages>
</xsl:template>
<xsl:template match="message">
<message>
<xsl:apply-templates select="*"/>
</message>
</xsl:template>
<xsl:template match="*">
<xsl:attribute name="{name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
代码说明:
<messages total="{count(message)}">用于生成元素<messages>,并为该元素添加了一个属性total。total属性的值为count(message),用来统计元素<message>的个数。
<xsl:attribute name="{name()}">用于生成新的属性。因为<xsl:attribute>中的name属性值为一个纯字符串,所以必须使用“{}”来进行XPath表达式的引用。XPath表达式“name()”输出上下文节点的名称,该名称即为生成的属性名。
<xsl:value-of select="."/>用于输出上下文节点的值,该值将被用做新生成属性的值。
输出结果如下所示。
<?xml version='1.0' ?>
<messages total="5">
<message id="001" from="Ann" to="Mike" title="Required Help"/>
<message id="002" from="Bob" to="John" title="Study together"/>
<message id="003" from="Stephen" to="Mark" title="Go Home"/>
<message id="004" from="Joe" to="Alex" title="Discovery an Island "/>
<message id="005" from="Kevvin" to="Kin" title="Join us"/>
</messages>
10.5.3 典型实例2——属性值难以获得
虽然可以利用“{}”所包含的XPath表达式来得到属性值,如清单10-19中的<messages total="{count(message)}">。但是,有时候属性值需要经过比较复杂的运算或判断才能得到,此时也必须使用<xsl:attribute>指令来生成属性。清单10-20所示的XML文档存储了一些音乐CD的信息。
清单10-20 存储了一些音乐CD信息的XML文档
<?xml version="1.0"?>
<catalog>
<cd>
<title>Empire Burlesque</title>
<artist>Alex</artist>
<country>USA</country>
<company>Columbia</company>
<price>9.50</price>
<year>1985</year>
</cd>
<cd>
<title>Hide your heart</title>
<artist>Bob</artist>
<country>UK</country>
<company>CBS Records</company>
<price>9.90</price>
<year>1988</year>
</cd>
<cd>
<title>Still got the blues</title>
<artist>David</artist>
<country>UK</country>
<company>Virgin records</company>
<price>10.20</price>
<year>1990</year>
</cd>
<cd>
<title>This is US</title>
<artist>Mike</artist>
<country>UK</country>
<company>Virgin records</company>
<price>15.20</price>
<year>1990</year>
</cd>
</catalog>
清单10-20所示的XML文档存储了若干CD的基本信息。元素<title>代表了CD的名称;元素<artist>代表了CD的演唱者;元素<country>代表了在哪个国家发行;元素<company>代表了发行公司;元素<price>代表了CD的售价;元素<year>代表了发行的年份。
现欲为每个元素<cd>添加一个属性reference,表示对价格进行衡量。如果高于10元,则为reference赋值 “dear”;如果等于10元,则为reference赋值“moderate”;如果低于10元,则为reference赋值“cheap”。此时,单纯使用“{}”包含一个XPath表达式已经无法解决,而只能使用<xsl:attribute>指令。相应的XSLT文档如清单10-21所示。
清单10-21 通过条件判断为属性赋值
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="catalog">
<xsl:apply-templates select="cd"/>
</xsl:template>
<xsl:template match="cd">
<cd>
<xsl:attribute name="reference">
<xsl:choose>
<xsl:when test="price > 10">dear</xsl:when>
<xsl:when test="price = 10">moderate</xsl:when>
<xsl:otherwise>cheap</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:copy-of select="*"/>
</cd>
</xsl:template>
</xsl:stylesheet>
代码说明:
<xsl:attribute name="reference">用于创建一个新的属性。该属性的名字为“reference”。
<xsl:when test="price > 10">dear</xsl:when>用于判断当价格大于10元时,为属性reference赋值“dear”。
<xsl:when test="price = 10"> moderate</xsl:when>用于当价格等于10元时,为属性reference赋值“moderate”。
<xsl:otherwise>cheap</xsl:otherwise>用于当价格不属于前两种情况时(小于10元),为属性reference赋值“cheap”。
输出结果如下所示。
<?xml version='1.0' ?>
<cd reference="cheap">
<title>Empire Burlesque</title>
<artist>Alex</artist>
<country>USA</country>
<company>Columbia</company>
<price>9.50</price>
<year>1985</year>
</cd>
<cd reference="cheap">
<title>Hide your heart</title>
<artist>Bob</artist>
<country>UK</country>
<company>CBS Records</company>
<price>9.90</price>
<year>1988</year>
</cd>
<cd reference="dear">
<title>Still got the blues</title>
<artist>David</artist>
<country>UK</country>
<company>Virgin records</company>
<price>10.20</price>
<year>1990</year>
</cd>
<cd reference="dear">
<title>This is US</title>
<artist>Mike</artist>
<country>UK</country>
<company>Virgin records</company>
<price>15.20</price>
<year>1990</year>
</cd>
10.5.4 典型实例3——所依附的元素为<xsl:element>
正因为属性的依附性,所以如果所依附的元素是使用<xsl:element>指令生成的,属性的生成也需要使用<xsl:attribute>指令。清单10-22所示的XML文档存储了若干标签信息。
清单10-22 存储了若干标签的XML文档
<?xml version="1.0"?>
<tags>
<tag>
<name>label</name>
<value>Please make your select</value>
</tag>
<tag>
<name>select</name>
<value>1</value>
</tag>
<tag>
<name>select</name>
<value>0</value>
</tag>
<tag>
<name>option</name>
<value>1</value>
</tag>
<tag>
<name>option</name>
<value>0</value>
</tag>
<tag>
<name>text</name>
<value>Do you have another demand</value>
</tag>
</tags>
在清单10-22所示的XML文档中,存储了若干<tag>元素的信息。元素<name>代表了标签的名称;元素<value>代表了标签的值。现欲将标签重新输出,以元素<name>的值作为新元素的名字,此时只能使用<xsl:element>。要将元素<value>转换为新元素的属性,此时只能使用<xsl:attribute>。相应的XSLT文档如清单10-23所示。
清单10-23 使用<xsl:element>和<xsl:attribute>生成XML文档
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="tags">
<form>
<xsl:apply-templates select="tag"/>
</form>
</xsl:template>
<xsl:template match="tag">
<xsl:element name="{name}">
<xsl:attribute name="value">
<xsl:value-of select="value"/>
</xsl:attribute>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
代码说明:
<xsl:element name="{name}">用于生成新的元素。元素的名字为XML源文档中元素<name>的值。
<xsl:attribute name="value">用于生成新的属性。属性的名字为“value”。
<xsl:value-of select="value"/>用于输出XML源文档中元素<value>的值。该值被赋给新生成的属性value。
新生成的属性value将被添加到生成的元素中。
转换结果如下所示。
<?xml version='1.0' ?>
<form>
<label value="Please make your select"/>
<select value="1"/>
<select value="0"/>
<option value="1"/>
<option value="0"/>
<text value="Do you have another demand"/>
</form>
10.5.5 典型实例4——生成XSLT文档
严格说来,在生成XSLT文档时,使用<xsl:attribute>来生成属性应该属于上一节所讲述的情形。但是,为了更好地查看如何利用<xsl:element>和<xsl:attribute>生成XSLT文档,本节将用一个典型的实例进行说明。清单10-24所示的XML文档存储了若干指令。
清单10-24 存储了若干指令信息的XML文档
<?xml version="1.0"?>
<instructions>
<instruction id="001">
<name>template</name>
<property>match</property>
<object>messages</object>
<parent>000</parent>
</instruction>
<instruction id="002">
<name>value-of</name>
<property>select</property>
<object>.</object>
<parent>001</parent>
</instruction>
<instruction id="003">
<name>copy-of</name>
<property>select</property>
<object>message</object>
<parent>001</parent>
</instruction>
<instruction id="004">
<name>apply-template</name>
<property>select</property>
<object>message</object>
<parent>001</parent>
</instruction>
</instructions>
在清单10-24所示的XML文档中,元素<instruction>代表XSLT指令。该元素含有一个id属性,用于代表指令的序列号;元素<name>用于代表指令的名称;元素<property>用于代表指令的属性名;元素<object>用于代表指令的操作对象;元素<parent>用于代表指令的父元素的id号。
★ 注意 ★
此处的XSLT指令即为XSLT元素,为了避免引起困惑,使用了XSLT指令来代替XSLT元素的说法。
现欲将其转换为一个XSLT文档(目标文档),那么相应的XSLT文档应该如清单10-25所示。
清单10-25 利用<xsl:element>和<xsl:attribute>生成XSLT文档
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="instructions">
<xsl:element name="xsl:stylesheet">
<xsl:apply-templates select="instruction[1]"/>
</xsl:element>
</xsl:template>
<xsl:template match="instruction">
<xsl:element name="xsl:{name}">
<xsl:attribute name="<xsl: {property}">
<xsl:value-of select="object"/>
</xsl:attribute>
<xsl:apply-templates select="../instruction[parent=current()/@id]"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
代码说明:
<xsl:element name="xsl:{name}">用于生成一个新的元素。该元素的名字为以“xsl”为前缀,加上XML源文档中的<name>元素的值。
<xsl: attribute name="xsl:{property}">用于生成一个新的属性。该元素的名字为XML源文档中<property>元素的值。
<xsl:value-of select="object"/>用于为新生成的属性赋值。该值为XML源文档中元素<object>的值。
转换结果如下所示。
<?xml version='1.0' ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="messages">
<xsl:value-of select="."/>
<xsl:copy-of select="message"/>
<xsl:apply-template select="message"/>
</xsl:template>
</xsl:stylesheet>
10.5.6 生成属性小结
本节详细讲述了如何使用<xsl:attribute>指令来生成新的属性,并列举了在哪些情况下只能使用该指令来生成属性。需要注意的是,如果元素中已有同名属性,那么在新生成的属性被添加到该元素时,原同名属性将被覆盖。另外,还可以利用<xsl:attribute-set>来生成属性集合。该XSLT元素的使用方法比较简单,下面为一个实例。
<xsl:attribute-set name="attributes">
<xsl:attribute name="aa">12</xsl:attribute>
<xsl:attribute name="bb">15</xsl:attribute>
</xsl:attribute-set>
以上代码定义了一个名为“attributes”的属性集合。该属性集合可以在生成元素时(例如,使用指令<xsl:copy>和<xsl:element>),利用use-attribute-sets属性进行引用。
10.6 输出指令——<processing-instruction>
在前面的内容中介绍了如何输出文本、元素和属性。在XML文档中,指令也是不可或缺的一部分,本节将讲述如何向目标XML文档输出指令。
10.6.1 <processing-instruction>语法
向目标XML文档输出处理指令,应该使用<xsl:processing-instruction>元素。其使用语法如下所示。
<xsl:processing-instruction name="process-name">
<!-- Content:template -->
</xsl:processing-instruction>
代码说明:
name属性,必选。用于指定处理指令的名字。
Content template用于生成处理指令的其他参数。
10.6.2 典型实例——创建处理指令
清单10-26所示的XML文档存储了一组消息,并使用一个元素<processing-instruction>来指定需要为该组消息添加的处理指令。
清单10-26 存储了一组消息和一条处理指令的XML文档
<?xml version="1.0"?>
<messages>
<processing-instruction name="xml-stylesheet">
<href>aa.xsl</href>
<type>text/xsl</type>
</processing-instruction>
<message id="001">
<from>Cisco</from>
<to>ANSI</to>
<title>The router article</title>
<body>Three thousands of routers should be sent to Sweden</body>
<description>Cisco router</description>
<dateTime>2005-12-01T00:12:09.001Z</dateTime>
</message>
<message id="002">
<from>MS</from>
<to>MySky</to>
<title>The resolution of problem</title>
<body>Some mistakes have been resolved</body>
<description>resolution</description>
<dateTime>2005-12-01T00:12:09.002Z</dateTime>
</message>
<message id="003">
<from>Carrefour</from>
<to>UPS</to>
<title>Required</title>
<body>Three thousands of TV set should be sent to China</body>
<description>TV set</description>
<dateTime>2005-12-01T00:12:09.003Z</dateTime>
</message>
</messages>
在清单10-26所示的XML文档中,元素<processing-instruction>存储了一条处理指令。属性name代表了该指令的名称;元素<href>和元素<type>代表了需要为处理指令生成的参数。将该XML文档中的元素< processing-instruction>转换为实际的处理指令的XSLT文档如清单10-27所示。
清单10-27 转换处理指令的XSLT文档
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="messages">
<xsl:apply-templates select="processing-instruction"/>
<xsl:text>
</xsl:text>
<xsl:copy>
<xsl:apply-templates select="message"/>
</xsl:copy>
</xsl:template>
<xsl:template match="processing-instruction">
<xsl:processing-instruction name="{@name}">
<xsl:apply-templates select="*"/>
</xsl:processing-instruction>
</xsl:template>
<xsl:template match="message">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="*">
<xsl:value-of select="name()"/>
<xsl:text>="</xsl:text>
<xsl:value-of select="."/>
<xsl:text>" </xsl:text>
</xsl:template>
</xsl:stylesheet>
代码说明:
<xsl:processing-instruction name="{@name}">用于创建一条处理指令。该处理指令的名称为属性name的值。
为<xsl:processing-instruction>创建的处理指令添加参数时,不能通过<xsl:attribute>。因为处理指令中的参数,虽然在形式上很像属性,但并不是属性。
需要注意的是,最后一个双引号的输出,需要追加一个空格。对于一条处理指令,相邻的参数之间需要用空格符分开。
转换结果如下所示。
<?xml version='1.0' ?>
<?xml-stylesheet href="aa.xsl" type="text/xsl" ?>
<messages>
<message id="001">
<from>Cisco</from>
<to>ANSI</to>
<title>The router article</title>
<body>Three thousands of routers should be sent to Sweden</body>
<description>Cisco router</description>
<dateTime>2005-12-01T00:12:09.001Z</dateTime>
</message><message id="002">
<from>MS</from>
<to>MySky</to>
<title>The resolution of problem</title>
<body>Some mistakes have been resolved</body>
<description>resolution</description>
<dateTime>2005-12-01T00:12:09.002Z</dateTime>
</message><message id="003">
<from>Carrefour</from>
<to>UPS</to>
<title>Required</title>
<body>Three thousands of TV set should be sent to China</body>
<description>TV set</description>
<dateTime>2005-12-01T00:12:09.003Z</dateTime>
</message>
</messages>
★ 注意 ★
<xsl:processing-instruction>元素生成的指令虽然是一个与<xsl:stylesheet>并列的顶级元素。但是<xsl:processing-instruction>不能出现在样式表的顶级层次中,只能出现在模板定义中。
10.7 输出注释——<xsl:comment>
处理指令和注释应该是XML文档中最容易被忽视的了。上一节讲述了如何输出处理指令,本节将重点介绍如何输出注释节点,输出注释节点需要用到指令<xsl:comment>。
10.7.1 <xsl:comment>使用语法
<xsl:comment>指令的使用语法如下所示。
<xsl:comment>
<!-- comment content --!>
</xsl:comment>
代码说明:comment content用来为新生成的注释节点提供内容。
10.7.2 典型实例——存储报价
清单10-28所示的XML文档存储了若干蔬菜的当日报价。
清单10-28 存储当日蔬菜报价的XML文档
<?xml version="1.0"?>
<Price_today >
<vegetable>
<name>cabbage</name>
<price>2.80</price>
<unit>kilo</unit>
</vegetable>
<vegetable>
<name>cucumber</name>
<price>4.00</price>
<unit>kilo</unit>
</vegetable>
<vegetable>
<name>aubergine</name>
<price>4.50</price>
<unit>kilo</unit>
</vegetable>
<vegetable>
<name>bamboo shoot</name>
<price>3.00</price>
<unit>kilo</unit>
</vegetable>
<vegetable>
<name>potato</name>
<price>2.00</price>
<unit>kilo</unit>
</vegetable>
<vegetable>
<name>tomato</name>
<price>4.10</price>
<unit>kilo</unit>
</vegetable>
</Price_today>
现欲为超过4元的蔬菜添加注释信息,注明价格过高。相应的XSLT文档如清单10-29所示。
清单10-29 为价格过高的蔬菜添加注释
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="Price_today">
<xsl:apply-templates select="*"/>
</xsl:template>
<xsl:template match="vegetable">
<xsl:copy>
<xsl:copy-of select="*"/>
<xsl:if test="price > 4.00">
<xsl:comment>
<xsl:value-of select="price"/>元 per <xsl:value-of select="unit"/>
is too dear for <xsl:value-of select="name"/> !
</xsl:comment>
</xsl:if>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
代码说明:
<xsl:if test="price > 4.00">用于判断当前蔬菜的价格是否大于4元。
<xsl:comment>用于输出一个注释节点。
<xsl:value-of select="price"/>元 per <xsl:value-of select="unit"/> is too dear for <xsl:value-of select="name"/> !,是一组串连字符串,用于生成注释节点的内容。
转换结果如下所示。
<?xml version='1.0' ?>
<vegetable>
<name>cabbage</name>
<price>2.80</price>
<unit>kilo</unit>
</vegetable>
<vegetable>
<name>cucumber</name>
<price>4.00</price>
<unit>kilo</unit>
</vegetable>
<vegetable>
<name>aubergine</name>
<price>4.50</price>
<unit>kilo</unit>
<!--4.50元 per kilo is too dear for aubergine ! -->
</vegetable>
<vegetable>
<name>bamboo shoot</name>
<price>3.00</price>
<unit>kilo</unit>
</vegetable>
<vegetable>
<name>potato</name>
<price>2.00</price>
<unit>kilo</unit>
</vegetable>
<vegetable>
<name>tomato</name>
<price>4.10</price>
<unit>kilo</unit>
<!--4.10元 per kilo is too dear for tomato ! -->
</vegetable>
10.8 输出消息——<xsl:message>
消息输出也是很多编程语言中都具备的功能。XSLT的消息一般都输出在控制台中,并可以控制是否需要停止程序的执行。
