无废话XML阅读笔记(四)
2008年6月8日
六.XSLT-XML 专属的转换语.
这章的重点是XSLT.要谈XSLT,得先从XSL谈起.
1. 另一种样式-XSL简介.
XSL(eXtensible Stylesheet Language)是专门为XML设计的样式,也是在CSS之外,另一个替XML穿戴打扮的选择.它一共分作两部分:第一部分负责将XML源代码转换为另一种格式,而第二部分(称作"FO" [Formatting Object;打样物件])则提供了大量的打样命令,可用来配合印刷或屏幕显像,精确地设定外观样式(如:字的大小,摆放的位置等),是一种所谓"device-independent"格式.第一部分的转换语法,可以用来为第二部分服务,将XML文件变形为打样命令.XSL的转换语法,并不限于将XML转成FO命令.事实上,XSL可以输出任何格式正确的XML文件.因为这个特性,我们可以用它来做以下几种形式的转换.
XML-->HTML 这是最常见的一种转换.转换出来的HTML文档,在格式上因为已经达到了格式正确的要求,在本质上非常接近XHTML.
XML-->XML 把一种格式的XML文件转换成另一种XML格式.有非常大的使用价值.例如:两个机关或企业之间以XML来传递信息,如果彼此使用XML格式有出入,可以用XSL来做调整.
XSL-->XSL XSL甚至可以将一个样式转换成另一种样式.(因为XSL,本身也就是XML的一个应用,所以XSL-->XSL在本质上不过是XML-->XML的一个特例而已).
(1). XSL和CSS不同的地方.
XSL采用的是转换的方式,将一种格式的XML,转换为另一种.而CSS则来自完全不同的理念:它不含任何转换动作,只针对XML文件中各个成分的外观属性一一加以设定.另一个区别是:XSL样式都是XML文件,完全照XML的语法来;相对地,CSS在语法上自成一个,和XML的写法大相径庭.
(2). XSL和CSS相同的地方.
XSL和CSS都属于样式表的一种.样式表是用设定外观的,它并不影响原来的XML源代码.XSL虽然用的是转换的方式,但是"转换"并不代表源代码遭到篡改.通常XSL转换后的输出码,是另存到一个新的档案,或暂存在浏览器的记忆体中.原来的XML档案内容保持不变.
2.XSLT入门.
(1). XSLT在网络上的应用模式.
Server端XML文件在下载到浏览器之前,先以XSLT转为HTML.
[1]. 动态即时产生: 在浏览器向Server送请求的时候,以XSLT处理软件将XML立即转换为HTML,送出.在网页中有动态资料要即时插入,或当XML文件是从数据库即时取的场合下,最适合这种运作模式.
[2]. 批次产生:如果XML文件事先都写好,一一存放在档案里,而非即时取得,或者随时不断更新,我们便可以用批次转换的做法,事先将HTML准备好.转换的工作则可以依据实际需要,在固定的时段自动化执行.这么做的好处是让Server资源可获得最大的节约.
Client端 如果浏览器直接支持XML和XSLT,XML源码和XSLT样式便可以以图像档一样,让浏览器直接下载下去,一切转换的工作由浏览器代劳.
(2). XSLT的转换流程以及工作原理.
XSLT转换必须由特别的软件来担任,这样的软件,统称为XSL处理器(XSL processor).在XSL处理器在工作之前,得先借XML解析器(内置或外部),替它把XSL样归和XML文件中的一个个物件和结构分析出来.
在XSLT中,有两个重要的观念,一个是源树(source tree),指的是转换前的XML文件的结构;另一个是结果树(result tree),代表转换的成品,结构依然是XML.XML处理器启动时,会先叫XML解析器替它解析XSL样式和要转换的XML文件.在掌握了样式中的各个XSL命令后,XSL处理器便开始依照样式的指示,在源树上"爬来爬去",一遇到合适的枝叶,就按样式的设定,吐出一段新枝叶(一个XML片段),一直到整颗树都走遍为止.有的时候,样式会指示处理器,将枝叶先修正一下再吐出来.所以经由XSL处理起一路产生的新枝叶,统统加到一块,就成了一株结果树.结果树可以输出到档案里储存起来,或者由浏览器显示在屏幕上.
(3). 支援XSL的软件.
支援XSL的软件,可粗略分为浏览器和转换工具(IBM AlphaWorks实验室出品的LotusXSL 和XSLT作者James Clark所写的xt)两类.在处理XSL时,尽量使用xt作为转换工具.在性能,速度,正确度和标准的支援程度上,xt都是很好的支持.
3. 细谈XSLT.
XSLT的语法和运行的方式有些诡异,可能需要一些时间才能渐渐适应.
(1). 成品预览.
<?xml version="1.0" encoding="gb2312"?>
<?xsl-stylesheet type="text/xsl" href="search_result.xsl"?>
<产品搜索>
<摘要>搜寻字符串:"滑鼠 键盘", 共找到2笔</摘要>
<产品>
<货号>00011</货号>
<品名>手不痛牌鼠标</品名>
<定价>$234</定价>
<说明页 网址="http://www.keona.cn/mouse/12345">上市发表会</说明页>
</产品>
<产品>
<货号>00022</货号>
<品名>打的响牌键盘</品名>
<定价>$567</定价>
<说明页 网址="http://www.keona.cn/key/654321">产品特性</说明页>
</产品>
</产品搜索>
透过XSLT我们可以将上面的XML码,转换成下面的HTML格式.
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>产品搜索结果</title>
<META http-equiv="Content-Type" content="text/html; charset=GB2312">
</head>
<body>
<h1>产品搜索结果</h1>
<p><b>摘要:</b>搜索字符串:"滑鼠 键盘", 共找到2笔</p>
<table>
<tr>
<th>品名</th><th>定价</th><th>说明页</th>
</tr>
<tr>
<td>手不痛牌鼠标</td><td>$234</td>
<td><a href-"http://www.keona.com/mouse/12345">上市发表会</a></td>
</tr>
<tr>
<td>打的响牌键盘</td><td>$567</td>
<td><a href-"http://www.keona.cn/key/654321">产品特性</a></td>
</tr>
</table>
</body>
</html>
以上的HTML代码为xt的实际输出结果.整个转换过程真正的主角,就是下面的XSLT代码.
<?xml version="1.0" encoding="gb2312"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- IE5只 接受
<xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl"> -->
<!-- IE5看不懂xsl:output -->
<xsl:output encoding="GB2312" />
<xsl:template match="/">
<html>
<head>
<title>产品搜寻结果</title>
</head>
<body>
<h1>产品搜寻结果</h1>
<p><b>摘要:</b><xsl:value-of select="*/摘要/"></p>
<xsl:apply-templates select="产品搜寻" />
</body>
<xsl:template>
<xsl:template match="产品搜寻">
<table>
<tr><th>品名</th><th>定价</th><th>说明页</th></tr>
<xsl:for-each select="产品">
<tr>
<td><xsl:value-of select="品名" /></td>
<td><xsl:value-of select="定价" /></td>
<td><a href="{说明页/@网址}"><xsl:value-of _fcksavedurl=""{说明页/@网址}"><xsl:value-of" _fcksavedurl=""{说明页/@网址}"><xsl:value-of" _fcksavedurl=""{说明页/@网址}"><xsl:value-of" _fcksavedurl=""{说明页/@网址}"><xsl:value-of" _fcksavedurl=""{说明页/@网址}"><xsl:value-of" _fcksavedurl=""{说明页/@网址}"><xsl:value-of" select="说明页" /></a></td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
(2). XSL样式与名称空间.
如同其他许多XML的应用,XSLT大力仰仗命名空间所提供的机制,把XSLT命令和其他XML标注隔开.样式所有的XSLT命令,都必须标明是隶属于"http://www.w3.org/1999/XSL/Transform"这个XSLT专用的命名空间,才能正常的工作.
在声明XSLT的名称空间时,最常见的做法是以"xsl"作为前置字符串.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">依照规定,所有的XSLT命令必须置于<xsl:stylesheet>..</xsl:stylesheet>区块里.也就是说,xsl:stylesheet这个元素界定了XSLT样式的内容;因为它是最外层的元素,命名的声明,自然也就摆在这个元素的标签里.在1999年的10月8日版的XSLT标准中,xsl:stylesheet又增加了一个不可省略的属性:version,用来标示样式所遵循的XSLT的语言版本.所有的XSLT命令,一律使用xsl作为前置字符串.
(3). XSLT运行细节.
XSL处理器按照样式的设定,在源树上寻找合适的节点,并适时产生新枝叶.这个样式的设定,在XSLT中正式称呼叫做"模板式(template ruels)".XSL处理器就是根据这些式子来寻找节点,产生新码的.
看个实例:
<?xml version="1.0" encoding="GB2312"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="GB2312" />
<xsl:template match="/">
<html>
<p>一个很空洞的模板,不怎么来劲!</p>
<html>
</xsl:template>
</xsl:stylesheet>
这是一份完整的XSL样式,里面只有一个模板式.我们看到,模板式以xsl:template这个元素来定义,该元素所包含的内容,不意外,正是XSLT中所谓的[模板(template;黑色文字)].xsl:template里面有个match="..."的属性,XSL样式正式借着这个属性,来告诉XSL处理器,该去找什么样的节点.在例子中,模板式指定对应的节点是"/",也就是在源树最顶层的跟元素.
XSL处理器在启动时,会先将所有列在样式中的模板式看过一遍,并牢记在心,然后便开始检视源树,而且肯定先从"/"(跟元素)下手,每对应到一个节点,就把相关的模板式的模板抄一份出来,加进要输出的文件里.
事实上,match的属性值不限于使用确切的节点名,其实它是一个对应式(pattern),有一套完整的语法,可用来选择样规里各个模板式对应到的节点.
还有要请注意的一点:模板的内容,必须是格式正确的XML片段,XSL掩饰的设定内容,是先经过XML解析器解析,然后才交给XSL处理器,因此,如果样式里有任何一段违背格式正确的原则,整个转换过程会再XML解析器那关就宣告失败.
当前节点和语境:
XSLT处理器会爬树,从这个节点爬到那关节点.每到一个新的节点,XSLT处理执行命令的语境(context)就跟着改变.这个新到的节点,在XSLT中的术语叫"当前节点(current node)",即处理器当时所在的节点.
当前节点应用实例---xsl:value-of;
XSL模板中的内容,除了能让XSL处理器一五一十的照抄,输出之外,还可以透过XSLT命令,对资料做排列,组合等处理.
<xsl:value-of ... />就是一个常用的XSLT命令,它通常是用来读取源树中某元素所包含的文字内容,或者是某个属性的值.标签中肯定要附上一个select属性.如同<xsl:template>中的match属性,在<xsl:value-of select=".." />里,select属性后面接的,也是对应式.XSLT对应式在对应的时候,以当前节点作相对位置的轴点.星号"*"会对应到位于当前节点下一层的所有元素的节点;换句话说,就是所有当前节点所在的元素的子元素."/"在对应式语法中是用来分隔节点层级的,就像/在网址和Unix系统中用来界定目录一样.合起来,"*/摘要"会对应到一个名叫"摘要"的元素节点,它的母节点刚好是当前节点的子元素节点.(该元素是空元素,请勿忘记<xsl:value-of .../>)
语境转移.
让当前节点层层下移.有两个XSLT命令-- xsl:apply-templates 和 xsl:for-each 可以用来达到这个目的,转移当前节点的位置.
模板套入-- xsl:apply-templates.
<xsl:apply-templates select="产品搜寻" />中有一个select对应式,这个命令告诉XSL处理器:把select对应到的各个节点一一当成是当前节点,并依次处理这些节点的模板.当<xsl:apply-templates>中的select属性省略时,XSL处理器则会以所有的现节点的子元素作为处理对象.(该元素是空元素,请勿忘记<xsl:apply-templates .../>).
<xsl:apply-templates .. />的出现,让转换流程起了变化.XSL处理器在这里转弯,先去执行其他的模板式,并且将产生的结果由<xsl:apply-templates .../>这点插入,一直到所有对应到的模板式都执行完毕,最后才回来执行原来的模板中剩下的部分.
xsl:for-each循环.
xsl:for-each是另一个会改变现节点和语境的命令.和xsl:apply-templates一样,xsl:for-each后面也有一个select对应式,用来选择心得现节点.在XSLT中,select对应式所对应到的节点不见得只有一个,而可能有好几个;如果不只一个,XSL处理器会按照他们在XML源代码中的顺序来一一处理.
(4). 属性值模板.
被大括号{}括起来的属性值时(<a href="{说明页/@网址}">123</a>),XSL处理器会把大括号中的区域,当作是属性值模板(attribute _fcksavedurl=""{说明页/@网址}">123</a>),XSL处理器会把大括号中的区域,当作是属性值模板(attribute" _fcksavedurl=""{说明页/@网址}">123</a>),XSL处理器会把大括号中的区域,当作是属性值模板(attribute" _fcksavedurl=""{说明页/@网址}">123</a>),XSL处理器会把大括号中的区域,当作是属性值模板(attribute" _fcksavedurl=""{说明页/@网址}">123</a>),XSL处理器会把大括号中的区域,当作是属性值模板(attribute" _fcksavedurl=""{说明页/@网址}">123</a>),XSL处理器会把大括号中的区域,当作是属性值模板(attribute" _fcksavedurl=""{说明页/@网址}">123</a>),XSL处理器会把大括号中的区域,当作是属性值模板(attribute" value templates)来处理.我们可以把属性模板想成是一个迷你模板.
"@",在XSLT中是用来对应属性名的.
(5).输出文字编码设定.
1999年8月13日版,XSLT草案中新增了一个xsl:output的命令,专门用来设定输出码的各项特性,如字符编码,缩排等.xml:output 中有一个encoding的属性,可以用来控制输出的字符编码.
4.XPath路径描述语.
XPath的目的是提供XSLT和XPointer(XML文件内部连接语)一个共同,整合的对应法,用来对应XML文件的各个部分,选择文件中的构成原件(元素,属性,文字内容...).
对应式 说明
-----------------------------------------------------------------------------------------------------------------------------------------
某元素 对应到当前节点下所有名为[某元素]的子元素.
* 对应到当前节点下的所有子元素.
*/某元素 对应到当前节点开始算起,所以名为[某元素]的孙节点.
@某属性 对应到一个附属于现节点,名为[某属性]的属性.
@* 对应到所有附属于现节点的属性.
text() 对应到当前节点的子元素中所有文字节点.
. 对应到当前节点.
.. 对应到当前节点的上一级.
某元素[1] 对应到当前节点下,第一个叫做[某元素]的子元素.
某元素[position()=1] 作用同上.
某元素/[@某属性="谋值"] 对应到当前节点下所有名为[某元素]的子元素,这个子元素必须含有一个[某属性]的属性,其属性值必须为[某职].
元素甲|元素乙 对应到当前节点下,所有名为[元素甲]和[元素乙]的子元素;|代表[或]的关系.
.//后世子孙 对应到当前节点下,所有名为[后世子孙]的元素;//符号代表可跨越数级.
祖宗//徒孙 对应到所有名为[徒孙]的元素,他们的上级必须是有一个叫[祖宗]的元素,而且[祖宗]还得是现节点的一个子元素.
5.迁就IE5
(1). IE5命名空间的差异:
http://www.w3.org/TR/WD-xsl(IE5) http://www.w3.org/1999/XSL/Transform(如今的).
(2). IE5不支持xsl:output.
(3). IE5不支持属性模板.
六.DTD--XML语汇的定义.
1. 元素类别的声明.
例子:
<!ELEMENT 名称 (#PCDATA)>
<!ELEMENT 作者 (#PCDATA)>
<!ELEMENT 售价 (#PCDATA)>
很明显地,ELEMENT之后放的是元素名,接着是它的[内容模型](也就是定义<元素>xxx</元素>之间xxx区域可以出现什么样的内容),#PCDATA是XML中预先定义好的标记,代表Parsable Character Data,即可解析的文字资料.
(1). 量子学.
DTD,一如在正规的表达式中,有所谓的量子(量词)最常用的有[?],[*],[+].
[?]:代表可有有零个或者一个. 0~1;
[*]:代表有多个(无限制)都可以. 0~无限;
[+]:代表至少必须有一个,但没有限制. 1~无限;
备注:DTD中的[?],[*],[+],[,]都是ASCII字符(半型)不要误用了中文的全型符号.
2.属性类别的声明.
在DTD中属性是<!ATTLIST..>来声明,ATT就是属性attbute的简写.
如:
<!ATTLIST 售价
货币单位(人民币|美元|港币) '人民币'>
最重要的是[货币单位]这行(称为[属性定义])共有三个要件.很明显地第一个是属性名,第二个是属性类别,最后是预设值或预设行为的描述(默认属性).如果属性不只一个的话,这样三个要件单元可以以每三个三个这样重复下去.
3. 统统放到一起--文件类别声明.
上面的元素,属性宣告一一设计好后,我们可以开始把他们整理起来,做成一个完整的DTD,并且把它和我们先前的XML文件连接在一起.有两种连接方式可以使用:一是外接,二是内嵌.如果用外接,我们只要将刚才写好的DTD,存到一个后缀名为.dtd的纯文字档中就可以.再配合:
<?xml version="1.0" encoding="GB2312"?>
<!DOCTYPE 推荐丛书 SYSTEM "book.dtd">
这样的链接方式,就可以了.我说[连接],是因为如果这个DTD文档不再同一台机器的同一个目录地下,则必须明确标明网址,而不能只写档案名,路径,否则会找不到.
或者,我们把DTD直接内嵌在XML文件中.这需要用到<!DOCTYPE ..>的声明,写法上有点CDATA区的感觉,像这样:
<!DOCTYPE 推荐丛书 [
<!ELEMENT 推荐丛书 (书籍*)>
...
]>
好了,到这里<<无废话XML>>的阅读笔记都整理完了,个人觉得<<无废话XML>>整体写的还是可以的,形象的举例.和网络式语言的调侃,让人在不知不觉的感觉上,渐渐熟悉了XML,但是由于台湾和国内的关系,很多专业术语都不一样,看起来的时候如果没有接触过XML的朋友可能会比较难看.不过,我还是在做笔记的时候注意到了这个问题,尽可能把它"翻译"过来了. 说老实话XML在目前开发中也起着十分重要的地位.如果你搞J2EE的,我建议你一定要牢牢的掌握这门知识.因为在J2EE领域中很多都用到了XML语言.如果你有了这个基础,相信再以后的J2EE道路上,你会走的更远更好.!