1、HTML 的缺点以及 XML 的产生原因
HTML 最初是一种表义的标记语言。Tim Berners-Lee 最初发明 HTML 的主要目的是为了与研究物理学的同事交流的方便(他当时是 CERN(欧洲粒子物理实验室)的研究人员)。超链接可以很方便地把关于物理学研究的各种资源链接起来。HTML 最初只是设计用来交流文本信息的,最初的 HTML 显示出来就是简单的文本,没有多少表示显示格式的标记。Marc Andreessen 发现了 HTML 和超链接的商业价值,开发出了世界上第一个图形界面的浏览器 NCSA Mosaic。Andreessen 随后与别人合作开了 Netscape 公司,并且制造出当时最成功的浏览器 Netscape Navigator。为了提高浏览器的表现能力,显示更加丰富的内容,有必要扩充 HTML,添加各种与显示格式有关的标记和属性,并且把对多媒体的支持加进去。例如 <table>、<center>、<font>、<img>、align、bgcolor 等等。Netscape Navigator 大为成功,尤其是在其后续版本推出强大的脚本语言 JavaScript、支持 plugin 并且能在页面中运行 Java Applet 后。甚至有人认为浏览器就是下一代的操作系统,将来开发的所有应用程序都应该是针对浏览器开发的(B/S 结构)。Netscape 是 1995 年 NASDAQ IPO(新股上市)最成功的公司,Marc Andreessen 一夜之间成为了亿万富翁。这引起了软件之王 M$ 的注意和警觉,M$ 认为 Netscape 正在对其构成威胁,一定要把 Netscape 击垮,占据互联网这个战略制高点。而互联网最重要的应用软件就是浏览器,于是 M$ 推出了自己的浏览器 Internet Explorer,并且采用免费和捆绑等不正当竞争手段,在 5~6 年(从真正展开竞争的 1997 年 IE 3.0 推出并与 Windows 捆绑到 2003 年 AOL 彻底放弃 Netscape 浏览器)的时间里将 Netscape 彻底赶出了浏览器市场。如今 Netscape 浏览器这个产品已经彻底灭亡,只留下了它开放源代码后浴火重生的 Mozilla 继续在与 IE 进行着顽强的抗争。不过由于 Linux 桌面应用已经走向成熟,所以将来 Mozilla 也会有很好的前景,事实上支持 Mozilla 已经提上了很多 B/S 结构软件开发商的议事日程。在 IE 与 Netscape 角力的几年时间里,两大浏览器都为了提高自己的表现能力各出奇招。他们不断推出新的只有自己才支持的、互不兼容的 HTML 标记。W3C 为了考虑工业界的需求也在不断地扩充 HTML 规范,以求各种浏览器能够有一个兼容的基础。HTML 规范经历了 2.0、3.2、4.0 等重要版本后已经非常庞大,完全背离了 HTML 最初设计主要用来表义的初衷,其中含有大量表示显示格式的标记和属性。结果是实现一个完全的 HTML 浏览器越来越困难和复杂,IE 和 Netscape 的规模都达到了 10M 以上。为 HTML 减肥的呼声越来越高。为了解决这个问题,以利于 Web 的健康发展,W3C 设计了 XML。
XML 的设计思想来自于古老的 SGML(其实 HTML 的设计思想也是来自于 SGML)。SGML 是 IBM 创造的一个用于出版业的文档格式标准,后来被 ISO 采纳作为国际标准(ISO 8879)。SGML 把文档内容与文档格式完全分离开,使得内容提供者的与排版人员的工作可以相互独立。SGML 是一种非常严谨的标记语言,但是 SGML 的问题是过于复杂,一个基于 SGML 的出版系统开发成本高昂,价格不菲,只有象 IBM 一样的大型企业才有能力购买这样的系统。SGML 过于重量级的特点使其完全不适合用于 Web 领域,而且 SGML 的设计完全是面向文档的,而不是面向数据的,大量基于 Web 的应用是面向数据的应用。W3C 参考 SGML 设计了新一代的标记语言 XML,XML——可扩展标记语言是一种元语言(我们使用的 Eclipse 也可以称做是一种元工具),它可以建造其它任意种类的标记语言。XML 实现了 W3C 最初设定的所有目标,轻量级、易于理解、扩展性好、平台中立、兼顾文档和数据。实际上 XML 规范只有几十页,只是 SGML 规范的 1/10 左右。
注:XML 1.0 规范的中文版可以在网上找到,地址是 http://lightning.prohosting.com/~qqiu/REC-xml-20001006-cn.html
2、XML 的外观
我们来看看 XML 是什么样:
java代码: |
1 <book id=" 1"> 2 <title>XML 高级编程</title> 3 <isbn> 7- 111- 07315- 0</isbn> 4 <author>Didier Martin 等</author> 5 <pages> 944</pages> 6 </book> |
好了,这个就是 XML,如果你熟悉 HTML 的话学习 XML 不非吹灰之力。在 XML 中的可以自定义任意的标记。
一段最低要求的 XML(文档或数据)只需要满足以下 4 个要求:
a、所有的起始标记都必须有结束标记。对于空元素可以采用这种形式表示:
<img src="..." />
b、标记区分大小写,起始标记与结束标记大小写必须一致。
c、多个起始、结束标记之间不能发生顺序错误,例如:
<title>
<isbn>
</title>
</isbn>
可以把 XML 看作一个堆栈,起始标记代表 push 操作,结束标记代表 pop 操作,所以必须采用后进先出的方式编写 XML。
d、所有的属性都必须有引号,引号可以是双引号也可以是单引号。一般采用双引号。
这就是最简单的 XML 的规则,简单吧?
XML 的内容当然不只这么多,可以看看《无废话 XML》以了解更多的内容。下面是一个更完整的 XML。
java代码: |
1 <?xml version=" 1. 0" encoding="ISO- 8859- 1"?> 2 <!DOCTYPE Configure PUBLIC "- //Mort Bay Consulting//DTD Configure 1.2//EN" "http://jetty.mortbay.org/configure_1_2.dtd"> 3 4 <!-- =============================================================== --> 5 <!-- Configure the Jetty Server --> 6 <!-- =============================================================== --> 7 <Configure class="org. mortbay. jetty. Server"> 8 9 <!-- =============================================================== --> 10 <!-- Configure the Request Listeners --> 11 <!-- =============================================================== --> 12 13 <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> 14 <!-- Add and configure a HTTP listener to port 8080 --> 15 <!-- The default port can be changed using: java -Djetty. port= 80 --> 16 <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> 17 <Call name="addListener"> 18 <Arg> 19 < New class="org. mortbay. http. SocketListener"> 20 < Set name=" Port"><SystemProperty name="jetty. port" default=" 8080"/></ Set> 21 < Set name="MinThreads"> 10</ Set> 22 < Set name="MaxThreads"> 100</ Set> 23 < Set name="MaxIdleTimeMs"> 30000</ Set> 24 < Set name="LowResourcePersistTimeMs"> 5000</ Set> 25 </ New> 26 </Arg> 27 </Call> 28 29 <!-- =============================================================== --> 30 <!-- Configure the Contexts --> 31 <!-- =============================================================== --> 32 33 34 <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> 35 <!-- Add a all web application within the webapps directory. --> 36 <!-- + No virtual host specified --> 37 <!-- + Look in the webapps directory relative to jetty. home or . --> 38 <!-- + Use the webdefault. xml resource for the defaults descriptor --> 39 <!-- + Upack the war file --> 40 <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> 41 < Set name="rootWebApp">root</ Set> 42 <Call name="addWebApplications"> 43 <Arg></Arg> 44 <Arg><SystemProperty name="jetty. home" default="."/>/webapps/</Arg> 45 <Arg>org/mortbay/jetty/servlet/webdefault. xml</Arg> 46 <Arg type=" boolean"> true</Arg> 47 </Call> 48 49 <Call name="addWebApplication"> 50 <Arg>/gwwweb</Arg> 51 <Arg>D:/WORK/gwwweb/webapps/default</Arg> 52 </Call> 53 54 <Call name="addWebApplication"> 55 <Arg>/publish</Arg> 56 <Arg>D:/WORK/gwwweb/webapps/publish</Arg> 57 </Call> 58 59 <Call name="addContext"> 60 <Arg>/</Arg> 61 < Set name="ResourceBase">D:/WORK/shljjnew/webapps/default</ Set> 62 <Call name="addHandler"> 63 <Arg>< New class="org. mortbay. http. handler. ResourceHandler"/></Arg> 64 </Call> 65 </Call> 66 67 <!-- =============================================================== --> 68 <!-- Configure the Request Log --> 69 <!-- =============================================================== --> 70 < Set name="RequestLog"> 71 < New class="org. mortbay. http. NCSARequestLog"> 72 <Arg><SystemProperty name="jetty. home" default="."/>/logs/yyyy_mm_dd. request. log</Arg> 73 < Set name="retainDays"> 90</ Set> 74 < Set name="append"> true</ Set> 75 < Set name="extended"> false</ Set> 76 < Set name="buffered"> false</ Set> 77 < Set name="LogTimeZone">GMT</ Set> 78 </ New> 79 </ Set> 80 81 <!-- =============================================================== --> 82 <!-- Configure the Other Server Options --> 83 <!-- =============================================================== --> 84 < Set name="requestsPerGC"> 2000</ Set> 85 < Set name="statsOn"> false</ Set> 86 87 </Configure> |
事实上,这个就是我们做开发时使用的 Jetty 的配置文件中的一部分(删除了一些内容以便于讲解)。
在这个文件中:
java代码: |
1 <?xml version=" 1. 0" encoding="ISO- 8859- 1"?> |
是所有 XML 文档中都必须有的 XML 声明,用途是告诉 XML 解析器文档所遵从的 XML 规范的版本和使用字符集。目前 XML 规范的最高版本就是 1.0。除了 ISO-8859-1 外(这个就是缺省的字符集,所以也可以不写),我们做开发时最常用的两个字符集是 GBK 和 UTF-8。
包含在 <? ... ?> 中的内容叫做处理指令(Processing Instruction,PI),处理指令不是 XML 的一部分,它的作用是为 XML 应用程序处理 XML 提供一些指示。注意,刚才说的 XML 声明虽然与处理指令很象,但是并不是处理指令。这个文档中并没有处理指令。我来举一个处理指令的例子,例如在文档中用到了 CSS 样式单,那么应该有这样一个类似的处理指令:
java代码: |
1 <?xml-stylesheet href="style. css" type="text/css" ?> |
这个处理指令就是告诉应用程序使用哪个样式单来显示 XML。
java代码: |
1 <!DOCTYPE Configure PUBLIC "- //Mort Bay Consulting//DTD Configure 1.2//EN" "http://jetty.mortbay.org/configure_1_2.dtd"> 2 |
是文档类型定义(DTD),指出这个 XML 是哪一类的标记语言,应该遵从哪一类标记语言的定义。除了用 DTD 的方式来定义标记语言类型外还可以用 Schema 来定义标记语言的类型。例如:
java代码: |
1 <wjml xmlns:xsi="http: //www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.xxx.com/dagl wjml.xsd" xmlns="http://www.xxx.com/dagl"> 2 |
这个 XML 中使用的是标准的 XML Schema。稍后我会详细讲解 DTD 和 Schema 的适用场合。
包含在 <!-- ... --> 中的内容是注释,也不是 XML 的一部分,这个与 HTML 是相同的。
其它的内容都很容易理解了,注意看这个文档是否完全满足我上面讲的 4 个条件。
3、处理 XML 的 API
基于 XML 的应用程序从逻辑上分成 3 个层次,下面是 XML 文档或数据,中间是 XML 解析器,上面是 XML 应用程序。XML 解析器的作用是读入 XML 文档或数据,将 XML 建造(build)为内存中的数据结构,便于应用程序处理,或者根据 XML 中包含的信息,调用应用程序提供的回调方法。由于 XML 是基于文本的,具有平台中立性,各种语言都有相应的 XML 解析器。
处理 XML 的 API 有这几种:DOM、SAX、JAXP、JDOM 和 DOM4J,这几种是最常用的,还有其它的一些不常用的 API。其中 DOM 可以用于多种语言,SAX 可以用于 Java 与 C++,JAXP、JDOM 和 DOM4J 只能用于 Java。一个 XML 解析器至少需要实现 DOM 或 SAX 两种 API 中的一种,不过现在常用的 Java XML 解析器都同时实现了 DOM、SAX 和 JAXP 3 种 API。JDOM 和 DOM4J 是由单独的包(jar)来实现的,使用 JDOM 和 DOM4J 需要得到标准的 XML 解析器的支持。目前在众多的 Java XML 解析器中使用最广的是 Xerces 和 Crimson,它们都是开源软件组织 Apache 软件基金会资助的开源软件。其中 Xerces 最初的代码来自 IBM 的贡献,Crimson 最初的代码来自 Sun 的贡献。JDK 1.4 以后的版本中已经包括有 XML 解析器,因此不再需要另外的 XML 解析器。IBM JDK 1.4 本带的 XML 解析器是 Xerces 的早期版本,Sun JDK 1.4 本身带的 XML 解析器是 Crimson 的早期版本(孩子总是自家的亲)。不过即使使用 JDK 1.4,你仍然可以使用其它的 XML 解析器。在我们的开发中全部使用 Xerces,主要是因为 Xerces 在所有 Java XML 解析器中功能是最丰富的,根据性能测试 Xerces 的性能要比 Crimson 好的多,而且我们在 Linux 上出于性能的考虑全部使用 IBM 的 JRE/JDK。
DOM 简介:
DOM 对于 XML 的处理方式就是把整个 XML 读到内存中形成一棵树状结构,然后用各种方法对这棵数进行遍历、插入、删除、修剪等操作。因为 DOM 是 W3C 的正式标准,所有的语言都有支持 DOM 的解析器,包括 Java、C/C++、Perl、JavaScript 等等。DOM 的优点是信息量丰富(全部都在内存中),而且可以随机访问,尤其是在处理前后相互关联的元素时非常方便。DOM 的缺点是 XML 应用程序在处理 XML 之前必须先由 XML 解析器把整个 XML 读进内存并生成树状结构,如果 XML 非常大,例如 10M,解析的过程是非常慢的。如果再加上 XSLT 转换(这是一种必须要使用 DOM 的操作)这类同样耗费资源的操作,可能会耗尽系统的内存资源。所以标准 DOM 只适合于中小型 XML 的处理。
SAX 简介:
为了更好地解决大型 XML 处理的问题,Java 开发人员发明了 SAX。SAX 采用事件驱动的方式来处理 XML,它的处理方式是:为每一个元素、属性、内容(这些都认为是事件)定义一个回调方法,这个回调方法由应用程序提供。解析器以数据流的方式读入 XML,当遇到某个元素、属性、内容时就调用相应的回调方法。SAX 的优点是处理效率高,适合处理大型 XML。缺点是 SAX 对 XML 是只读的,不能够对 XML 进行写操作,而且 SAX 处理 XML 中前后相互关联的元素时也没有 DOM 方便,因为应用程序必须自己保留以前事件的状态信息。但是 SAX 还是取得了巨大的成功,后来 SAX 还被移植到了 C++ 等语言中。SAX 更详细的优缺点可以查看《XML 高级编程》第 6 章的内容,我们这里有这本书的电子版。
JAXP 简介:
你们对 XML 熟悉了以后可能会经常听到 JAXP 这个词。JAXP 其实不是一个独立的 XML API,它的作用是为了解决不同的 XML 解析器之间的兼容性问题的。在 Sun 推出 JAXP 之前,商业公司(IBM、Oracle、Sun,etc.)、XML 专业研究者以及开源软件爱好者开发出来多种多样的 XML 解析器。这些解析器有的仅仅实现了 DOM API,有的同时实现了DOM 和 SAX。在我学习 XML 的过程中,我所知道的 Java XML 解析器就有 7、8 种。这些不同的 XML 解析器各有各的特长,它们虽然都遵从 W3C DOM 或 SAX 的规范,但是或多或少存在着一些差别,这使得为一种解析器编写的应用程序移植到另一种解析器变得有些困难(也不是非常困难,只是不太方便)。为了解决这个问题,Sun 在 DOM、SAX 的 API 之上加了一个抽象层(基本上就是加了一个抽象工厂的设计模式,如果你们对设计模式有所了解的话),这就是 JAXP。JAXP 主要采用反射的方式来工作,可以动态加载所使用的 XML 解析器。使用 JAXP 来做 XML 开发,可以完全忽略不同的 XML 解析器之间的差别,不需要修改代码就可以更换成另外的 XML 解析器。JAXP 目前已经得到大多数 Java XML 解析器的支持。除了封装 DOM、SAX 的那些工厂类外,JAXP 还包含用于 XSLT 转换的 API(也是一些工厂类),这些功能在 javax.xml.transform 这个包里。
JDOM 简介:
除了 SAX,Java 开发人员还发明了 JDOM。虽然名字里有 DOM,JDOM 其实与 DOM 毫无关系。JDOM 严格说来其实是一种对象模型,除了处理 XML,还可以用于很多其它用途。JDOM 的输入可以有多种格式,输出也可以有多种格式。这些格式包括 XML 文件或数据流、Java 属性文件、标准 DOM 树等等。JDOM 利用 SAX 或 DOM(一般是用 SAX,极少用 DOM)读入 XML 中的信息后在内存中生成自己的数据结构,就是通过组合(composition)关系嵌套在一起的一系列对象,然后用 Java 程序员最习惯的面向对象的方式来处理这些数据,处理结束后可以非常方便地输出成各种格式。和 DOM、SAX 相比,用 JDOM 来做 XML 开发更加容易(代码量更少)。JDOM 的主要的缺点是最初的设计未考虑性能问题,因此性能很差,而且全部使用具体类来实现导致了灵活性较差,但是考虑到易用性和开发效率,JDOM 仍然得到了广泛的使用。
DOM4J 简介:
由于 JDOM 本身存在着一些设计缺陷,开发到一半,JDOM 的一些开发人员分了出来重新启动了一个开源项目 DOM4J。DOM4J 与 JDOM 所要达到的目标是一样的,即提供一套比 DOM、SAX 使用更加方便的处理 XML 的 API,同时避免 DOM、SAX 的主要缺点。DOM4J 重新做了设计,完全基于接口和多态,因此提高了灵活性。DOM4J 在概念上对 XML 的理解与 DOM 非常相似,只要你理解了 DOM,你就可以轻而易举地理解 DOM4J,实际上 DOM4J 可以看做是一个更好的 DOM 实现。而且 DOM4J 保持了 JDOM 的易用性,性能上也比 JDOM 有了非常大的提高。最值得一提的是 DOM4J 完全支持 XPath,看看 DOM4J 的这段代码:
java代码: |
1 List list = document. selectNodes ( //foo/bar ); 2 Node node = document. selectSingleNode ( //foo/bar/author); 3 |
这与我们前台写的 JavaScript 是非常相似的,我们学会 DOM4J 是几乎不需要花什么时间。
DOM4J 与 JDOM 一样,通过 SAX 或 DOM(一般用 SAX)读入 XML 中的信息在内存中生成自己的数据结构,因此 DOM4J 至少需要一个实现了 SAX 的 XML 解析器,我们可以直接使用 Xerces。
由于 DOM4J 具有易用性、性能、灵活性、功能强大等多方面的优势,今后我们如果需要在服务器端做 XML 开发,DOM4J 将是我们主要采用的工具。
关于这几种 API 的详细内容,请参考《Java 与 XML》和《XML 高级编程》。关于 DOM4J,主要有这些资料:
http://dom4j.sourceforge.net/faq.html
http://www.csdn.net/develop/article/22/22753.shtm
听了我上面的介绍你们可能会觉得 DOM 是比 SAX 更基础的 API,因为它是 W3C 的标准,所有的语言都支持,而 SAX 的使用仅局限于少数几种语言。某种程度上你是对的,但是在 Java 的世界里,SAX 是比 DOM 更加基础的 API。由于 SAX 处理效率很高,SAX 的应用范围比 DOM 更广。例如:我们中间件的低层框架 Avalon(也是 Apache 软件基金会的产品)处理 XML 配置文件时使用的就是 SAX。另外,因为 W3C 并没有规定在内存中如何生成一棵 DOM 树(W3C 只规定了如何操作这棵 DOM 树),Xerces 采用高效率的 SAX 来读入 XML,然后生成 DOM 树。因此当你在用 Xerces 做 DOM 开发时发现经常需要捕获 SAXException 就没什么可奇怪的了。JDOM 和 DOM4J 通常也是使用 SAX 读入 XML,然后生成自己的数据结构。对于 JAXP、JDOM 和 DOM4J 来说,DOM 和 SAX 都是基础的 API。
在我们公司做开发最常接触的 XML 开发是使用 JavaScript 做 DOM 开发,因为时间有限,所以我今天只详细讲一下在前台使用 JavaScript 和 DOM API 做 XML 开发的过程。今天的目的主要是让大家对于 XML 相关的知识有一个整体的了解。
大家知道 JavaScript 是嵌入在浏览器中的,创建 DOM 树不是 JavaScript 的责任,浏览器已经创建好了,另外通常 JavaScript 也不能直接读写 XML 文件(在权限许可的情况下 JavaScript 可以读写本地文件)。JavaScript 处理的 XML 数据有两个来源,一个是来自于页面中的 XML 数据岛,另外一个是来自于从 XMLHTTP 接口接收的后台发来的 XML 数据。注意:在浏览器中的 DOM 有两种,HTML DOM 和 XML DOM。如何处理 HTML DOM 在普通的 JavaScript 教材(《JavaScript 权威指南》等等)中已经讲得很详细了,我这里只详细讲一下如何处理 XML DOM,下面所说的 DOM 都是指 XML DOM。在讲 XML DOM 之前我首先要讲一下 XPath。
什么是 XPath?简单地说,XPath 就是定位 XML 中某些节点(元素、属性、内容、处理指令、文档类型定义、注释)的方法。XPath 的目的是为 XSLT 和 XPointer 提供一个共同的、整合的语法,用来对应 XML 的各个部分,选择 XML 中的某个或某些节点。XPath 是在 DOM 树中查找节点、做 XSLT 转换、定义文档内部指针(XPointer)的基础。有时候也把一个符合 XPath 规范的表达式称做一个 xpath。我们通常把 XPath 表达式的结果称为一个节点集(node set)。节点集能够被转换、复制、忽略或执行其它的合法操作。XPath 除了包括定位语法外还包括很多函数定义,这些函数分成 4 类:节点集函数、字符串函数、布尔函数和数值函数。节点集函数,例如 position() 和 count(),通常以节点集作为输入(以 XPath 表达式的形式),然后做进一步处理。其它 3 种类型的函数,例如 substring()、not() 和 round() 提供基本的字符串处理、逻辑运算和数值运算的功能。关于 XPath 中各种函数定义的详细内容可以参考《XML 高级编程》这本书。所有这些表达式语法或函数定义都是 XPath 规范或实现的一部分。
好了,长话短说,我在这里主要讲一下 XPath 如何使用。其实我们需要知道的基本上就是《无废话 XML》中表 7.1 的内容。
XPath 的定位语法与操作系统目录结构的语法非常相似。也分成两种,相对路径与绝对路径。相对路径从当前正在处理的节点开始,绝对路径从文档的根节点开始。我来举些例子:
java代码: |
1 A 对应当前节点下所有名为 A 的子元素。 2 * 对应当前节点下所有子元素。 3 */A 对应自当前节点开始,所有名为 A 的孙元素。 4 @A 对应一个附属于当前节点,名为 A 的属性。 5 @* 对应所有附属于当前节点的属性。 6 text ( ) 对应当前节点的子元素中的所有文本节点。 7 . 对应当前节点 8 .. 对应当前节点的父节点。 9 A [ 1 ] 对应当前节点下,第一个名为 A 的子元素。 10 A [position ( )= 1 ] 作用同上。 11 A/ [@B=" true" ] 对应当前节点下所有名为 A 的子元素,这个子元素必须含有一个名为 B 的属性,其属性?当匦?为 " true"。 12 A|B 对应当前节点下,所有名为 A 或 B 的子元素;| 代表“或”的关系。 13 . //A 对应当前节点下,所有名为 A 的元素;// 符号代表可跨?绞?。 14 A //B 对应到所有名为 B 的元素,它们的上级(可跨?绞叮?必须有一个名为 A 的元素,而且 A 元素必须是当前节点的子元素。 15 /A 对应根节点下所有名为 A 的子元素。 16 //A 对应根节点下所有名为 A 的元素。A 元素可以在任意层次。 17 |
我们在做前台开发时,有两个方法会用到 XPath,selectSingleNode() 和 selectNodes()。这两个方法是 IE 的扩展,不属于 W3C DOM 规范,但是使用起来非常方便,所以我们在 DOM4J 中也可以看到这两个方法。
java代码: |
1 hwn.__drTags = sr1. selectSingleNode ("./tag" ); 2 var cxn = xn. selectNodes (". /*[not(@m:f) or @m:f!='d']"); |
上面是从我们的前台开发框架中 copy 出来的代码。
selectSingleNode 用于获得一个节点,如果有多个节点满足条件,返回的是第一个节点。
selectNodes 用于获得节点集,返回的结果是包含所有满足条件节点的数组。
第二条语句的参数看起来有些复杂,它的意思是找到 xn 节点下所有不包含 m:f 属性或者包含 m:f 属性,但属性值不等于 'd' 的所有子元素。
下面我来详细讲解 XML DOM 在 JavaScript 中的实现。
在浏览器中,无论 XML 的数据来源如何,最后都会由浏览器生成一棵 DOM 树。这棵 DOM 树所对应的对象类型为 XMLDocument,这棵 DOM 树上的所有节点(包括根节点)都是 XMLNodes 类型的对象。事实上 XMLDocument 也是 XMLNodes 的子类,所以你可以在任何一种 XMLNodes 派生出的对象上调用 selectSingleNode 和 selectNodes。
数据来自 XML 数据岛:
例如在页面中有这样一个 XML 数据岛,
<xml id="book">
...
</xml>
var xd = book.XMLDocument;
通过外部 XML 文件创建新的 DOM 树:
var xd = new ActiveXObject("Microsoft.XMLDOM");
xd.load(data.xml);
或者将外部文件中的数据加载到 XML 数据岛:
var xd = book.XMLDocument;
xd.load(data.xml);
注意:load() 这个方法的参数可以是任意合法的 URL,不一定限制为文件名。
将数据保存到 XML 文件用
xd.save(data.xml);
通过字符串创建新的 DOM 树:
var xd = new ActiveXObject("Microsoft.XMLDOM");
xd.loadXML("<"+hd.__hwnodes.style.rootTag+"></"+hd.__hwnodes.style.rootTag+">");
数据来自 XMLHTTP:
var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
xmlhttp.open("GET","/hwsvr2/qe",false);
xmlhttp.send(null);
var xd = xmlhttp.responseXML;
得到 XML 文档的根节点:
var xn = xd.documentElement;
我再来说说在 Mozilla 中创建 XML DOM 树的方法,将来我们有可能会为 Mozilla 开发页面,所以对于如何保持脚本的兼容性有些了解是有好处的,这部分内容不是必须掌握的内容。
Mozilla 中并没有 IE 的 XML 数据岛这个概念,不过在 Mozilla 中模拟 XML 数据岛是非常容易的,这里有这方面的资料:
http://www.mozilla.org/xmlextras/xmldataislands/
感兴趣的可以看看。
Mozilla 中内建有对 XML DOM 的支持,并没有使用 ActiveX 之类外挂的方式(被 Mozilla 开发者讥笑为打补丁的方式)。在 Mozilla 中访问创建好的 DOM 树的方法与 IE 中基本上是一样的,所不同的是创建 DOM 的过程,Mozilla 采用的方法都是符合 W3C 标准的方法:
通过外部 XML 文件创建新的 DOM 树:
var xd= document.implementation.createDocument("","",null);
xd.load("data.xml");
第一个参数是名字空间的 URL,第二个参数是 DOM 根元素名称,第三个参数是定义 XML 格式的 DTD。在这里这 3 个参数都可以不提供。
Mozilla 中没有 loadXML() 这个简单的方法,但是可以手工为 Mozilla 添加这个方法,详细信息在这里:
http://webfx.eae.net/dhtml/xmlextras/xmlextras.html
数据来自 XMLHTTP:
var xmlhttp=new XMLHttpRequest();
xmlhttp.open("GET","/hwsvr2/qe",false);
xmlhttp.send(null);
var xd = xmlhttp.responseXML;
得到 XML 文档的根节点:
var xn = xd.documentElement;
由上面可以看到只要创建了 XML DOM 树后,访问方法 IE、Mozilla 是完全一样的,这部分的差异非常容易通过封装的方法屏蔽掉。Mozilla 没有提供 selectSingleNode() 和 selectNodes() 两个方法,以前我写过这两个方法在 Mozilla 上的实现,贴在论坛上,感兴趣的可以看看:
http://forum.hibernate.org.cn/viewtopic.php?t=965
下面我来讲讲访问 DOM 树常用的方法。
刚才我们说到过,DOM 树的访问有两个级别,文档级别和节点级别。
在文档级别,XMLDocument 对象上有这些方法:
abort():终止正在进行的异步下载
getElementByTagName():根据标记名称查找当前节点的子元素。
load():从指定位置下载 XML。
save():将 XML 保存到指定位置。
XMLDocument 对象上有这些属性:
async:指出 XML 是否可以异步下载。
doctype:返回 XML 的 DTD 节点。
documentElement:返回 XML 的根元素。
parseError:返回包含 parse error 信息的对象。
在节点级别,XMLNodes 对象上有这些方法:
createElement():在当前节点下创建一个子元素。
createTextNode():在当前节点下创建一个内容节点。
appendChild():在当前节点上添加新的子节点。
cloneNode():复制一个节点,返回值为生成的新节点。
hasChildNodes():当前节点是否为叶节点。
insertBefore():在某节点前插入新的节点。
removeChild():删除当前节点的子节点。
replaceChild():替换当前节点的子节点。
XMLNodes 对象上有这些属性:
childNodes:包含所有的子节点数组。
firstChild:当前节点的第一个子节点
lastChild:当前节点的最后一个子节点
nextSibling:当前节点的下一个兄弟节点
nodeValue:节点值
ownerDocument:当前节点对应的 XML 文档
parentNode:当前节点的父节点。
parentElement:父元素的句柄。
xml:从当前节点开始子树所生成的 XML。这个属性只有 IE 支持。
对于所有的元素节点,可以使用这两个方法:
setAttribute():设置当前元素的属性。
getAttribute():获得当前元素的属性。
clearAttributes():清除当前元素的所有属性。
另外还有 selectSingleNode() 和 selectNodes(),这两个方法其实是最常用的了。由于 XML DOM 中可以使用强大的 XPath 查找你想要查找的任意节点,比 HTML DOM 中仅能用 all()、getElementById()、getElementByTagName() 几个功能有限的方法方便得多。
只要你理解了 DOM 的概念,并且没有忘记以前学的数据结构的知识,上面的这些方法理解起来是非常简单的,我就不细讲了,关键是要经常使用,熟能生巧。
4、XML 有效性验证的方法和名字空间
符合第 2 部分讲的 4 个条件的 XML 叫做格式正确的(well-formed)XML。还有一种有效的(valid)XML。什么叫做有效的 XML 呢?
有效的 XML 就是通过某种格式定义来规定这个 XML 中只能有哪些元素、这些元素应该按照什么顺序出现,每个元素有哪些属性,这些属性的数据类型、取值范围等信息。然后这一类的 XML 只要通过验证符合这个格式定义就认为是有效的 XML。那么为什么 XML 需要通过验证,这主要是电子商务的需要。举个例子,如果你需要通过 Web 方式与客户进行 B2B 交易,你把电子形式的发票发给客户,客户也会把他们的发票发给你,但是两种发票的格式不同,就好象你在说汉语他在说英语,彼此无法正常交流,所以必须要有一个统一的格式(标准)才能够开展正常的电子商务活动。XML 就是靠不同类型的格式定义来建造不同的标记语言的,每一种格式定义(标记语言)叫做一种词汇表(vocabulary)。
XML 的格式定义有很多种方法,包括 DTD、XML Schema、RELAX NG 等等。DTD 是参考 SGML DTD 创造出的 XML 格式定义方法。DTD 的格式定义采用与 XML 不同的语法,这使得很难直接用解析器来解析 DTD,也很难动态(runtime)验证 XML 的有效性。W3C 后来又创造了 XML Schema。Schema 是一种新型的 XML 格式定义方法,它完全采用 XML 语法,便于解析器处理,而且对于数据格式的定义更加严格和精确,所以 Schema 更加适合面向数据的应用。现在的 XML 解析器一般都支持做 DTD 验证,也有同时支持做 DTD 和 Schema 验证的,例如我们使用的 Xerces。那么有了 Schema 是否我们就可以完全抛弃 DTD 呢?答案是否定的,由于 DTD 来自于 SGML,它非常适合面向文档的应用(SGML 完全是为文档处理而设计的)。定义相同的 XML 格式,DTD 定义要比 Schema 简练的多,Schema 定义则显得很冗长,所以 DTD 更加适合面向文档的应用,不过把 Schema 用于面向文档的应用也不会有多大的问题。因为 Schema 的格式定义很烦琐,所有有人开发出了其它的格式定义方法,其中比较有前途的是 RELAX NG。RELAX NG 是一种以 RELAX(由日本人开发)与 TREX(由 XML 界的权威 James Clark 开发)为基础的模式语言。它的基本思想与 Schema 相同,也采用 XML 格式,所以程序处理起来也很方便,而且它的语法比 Schema 要简单的多。但是目前 RELAX NG 还不是 W3C 的标准,所以大多数解析器都不支持(需要使用专门的软件包)。我的看法是 Schema 可能会在今后参考 RELAX NG 而得到简化,所以我们还是应该更多地使用 Schema。
现在我来讲一下名字空间这个非常重要的概念。名字空间(namespace)在 XML 中的作用其实与 package 在 Java 中的作用非常象。XML 是一种元语言,可以建造出无穷多种标记语言。有些时候需要在同一个 XML 文档中混用不同类型的标记语言,但是这些不同类型的标记语言中可能有重复的标记,例如:我为书籍分类建造了一种标记语言,其中有 <title> 这个标记;我又为影片分类建造了一种标记语言,其中也有 <title> 这个标记。但是在我的 XML 文档中需要同时包含书籍和影片两类的数据(假设这个 XML 文档其实是用户的一个购物车,他同时买了书和 DVD),我如何区分一个 <title> 究竟是书籍的 title 还是影片的 title 呢?这时候名字空间就可以帮你了。名字空间就是一个元素前缀与 URI 之间的一种映射关系。名字空间是用一个 XML 元素加上一个前缀组成的,比如 <book:title> 和 <picture:title>。这样 XML 解析器可以在不使用不同的元素名字的情况下,区分上述两个元素名字。元素前缀可以为任意字符串,最终一个 XML 文档中的每个元素前缀要映射到一个唯一的 URI,这个 URI 就是不同的 XML 词汇表的标识。除了用于元素,名字空间同样也可以用于属性(下面我们马上可以看到)。名字空间经常在 XML 文档中使用,也可以在 Schema 及 XSLT 样式单或者 XML 有关的规范中使用。下面我们看一下名字空间的具体使用方法:
java代码: |
1 <?xml version=" 1. 0" encoding="gbk"?> 2 <DocRoot xmlns:xsi= 'http://www.w3.org/2001/XMLSchema-instance' xsi:schemaLocation="http: //www.xxx.com/dagl ./../mlzx/schemas/dz/wjnr.xsd" xmlns="http://www.xxx.com/dagl"> 3 ... 4 </DocRoot> |
我们可以看到在这个 XML 中声明了两个名字空间。声明名字空间使用 xmlns:前缀=URI 的形式,其中前缀也可以没有,用来设置 XML 文档缺省的名字空间。可以在任何元素上声明名字空间,声明后名字空间在该元素管辖范围(作用域)内起作用。在这个 XML 文档中使用了 Schema 来做有效性验证,因此必须声明 Schema 的名字空间。前缀可以是任意字符串(通常使用 xsi),但是这个名字空间所对应的 URI 是固定的,代表的是一个 Schema 的实例(Schema 与 XML 文档的关系就象是类和对象的关系一样)。名字空间声明后立即可以使用,所以在后面紧接着用这个名字空间前缀 xsi 设置了一个属性 schemaLocation,这个属性用来设置 Schema 文件的路径。xmlns="http://www.xxx.com/dagl" 中没有前缀设置了文档的缺省名字空间,也就是说在本文档中所有没有前缀的元素、属性都属于这个缺省的名字空间。
我们再来看看在 Schema 文件中使用名字空间的情况:
java代码: |
1 <?xml version=" 1. 0" encoding="gbk"?> 2 <schema xmlns="http: //www.w3.org/2001/XMLSchema" 3 xmlns:dagl="http: //www.xxx.com/dagl" 4 targetNamespace="http: //www.xxx.com/dagl" 5 elementFormDefault="qualified"> 6 7 <complexType name="attrType"> 8 <simpleContent> 9 <extension base="string"> 10 <attribute name="code_category" type="string" /> 11 <attribute name="id" type="string" /> 12 </extension> 13 </simpleContent> 14 </complexType> 15 16 <complexType name="dmType"> 17 <sequence> 18 ... 19 <element name="flh" type="dagl:attrType"/> 20 ... 21 </sequence> 22 </complexType> 23 ... 24 </schema> |
这个摘自刚才那个 XML 文档对应的 Schema。我们看到这个 Schema 中除了缺省的名字空间外还声明了一个前缀为 dagl 的名字空间,而且这个名字空间的 URI 必须与 targetNamespace 的 URI 相同。我把 Schema 规范设置为缺省名字空间是为了简化编写 Schema 定义,如果不这样做,那么每一个 complexType、sequence、element 都需要加上前缀(通常使用 xsd),那样是很麻烦的。targetNamespace 用来定义相应的 XML 文档(XML 实例,xsi)应该遵守的名字空间,也就是 XML 实例的缺省名字空间。为什么要这样做我不细讲了,这里的规则就是在 Schema 中自定义的类型(这里是 attrType)使用时必须加上名字空间前缀,这个前缀对应的名字空间 URI 必须与 targetNamespace 的 URI 相同。
现在我们对于 DTD、Schema、名字空间的作用都有了比较清晰的了解。在这一部分需要重点掌握的是名字空间的作用和使用方法。做 DTD、Schema 验证的具体方法可以看我发在 bbs 上的文章:介绍一下做 Schema 验证和 DTD 验证的方法
http://forum.hibernate.org.cn/viewtopic.php?t=859
用 Xerces 来做 DTD 和 Schema 验证是非常简单的。想要更详细地了解 DTD 和 Schema 的开发方法可以看《XML Schemas》和《XML 高级编程》这两本书。
因为性能上的原因,大部分情况下我们做 XML 开发时是不需要验证 XML 的有效性的。不过我们将来有可能会开发自己的 XML 词汇表,所以我们还是有必要学习相关知识的。我们将来主要会采用 Schema,需要重点学习这方面的知识,DTD 仅仅作为需要了解的知识。还有一个采用 Schema 而不采用 DTD 的重要原因是 DTD 不支持名字空间。由于 DTD 来自于 SGML,SGML 中完全没有名字空间的概念,因此 DTD 也完全不能识别 XML 中的名字空间。如果在 XML 中使用了包含了前缀的元素名,例如 <book:title>,用 DTD 做有效性验证时就会出错,DTD 顽固地认为你必须在相应 DTD 文件中定义一个 book:title 元素才行(它不会认为这个 book:title 其实就是一个 title)。而 Schema 完全支持名字空间,正是因为这个原因,而且考虑到名字空间的重要性,虽然目前 Schema 还存在缺陷,我们还是应该坚持使用 Schema 而不使用 DTD。
在目前阶段,只需要掌握好名字空间就可以了。Schema、DTD 等内容可以在需要的时候再去学习。
5、XML 与显示的结合
第 1 部分说到过,XML 的设计目标就是把内容与显示格式分离开。其实只有面向文档的应用才有显示的需求,面向数据的应用不需要定义显示格式。把内容与显示格式分离不仅有利于面向文档应用的开发,更有利于面向数据应用的开发。我来举个面向数据应用的例子,假设你做了一个 Web 搜索引擎(或者叫做网络爬虫),如果页面采用 HTML 来开发,当你的程序读到这样的信息:
java代码: |
1 <tr> 2 <td>XML 高级编程</td> 3 <td> 95. 00</td> 4 </tr> |
你的程序能辨别出这其实是一本书的名称和价格吗?尤其是当这些单纯表示格式的 <tr>、<td> 以不规则方式嵌套了很多层的时候,开发出一个支持 HTML 全文检索的搜索引擎简直是一场灾难。但是如果这些页面都是采用 XML 来开发的,并且内容与显示格式完全分离,当你的程度读到这样的信息:
java代码: |
1 <book> 2 <title>XML 高级编程</title> 3 <price> 95. 00</price> 4 </book> |
就可以非常容易地辨别出这是一本书的名称和价格,尤其是当这个 XML 页面使用了通用的词汇表并且通过了有效性验证时。所以 XML 就是搜索引擎开发者的福音,XML 还会催生出一大堆面向数据的应用,而这类应用在 XML 出现前是根本无法实现的。
言归正传,现在我们来谈谈面向文档的应用。那么显示格式用什么来定义呢?有两种方法,CSS 和 XSLT,XML 文档要在浏览器中显示必须结合 CSS 或者 XSLT 的样式单。XML 文档与 HTML 一样可以用 CSS 来定义显示格式(这里有个例子:http://www.wespoke.com/atom.xml)。
我们先看一下上面这个例子中如何用 CSS 来定义显示格式:
java代码: |
1 <?xml version=" 1. 0" encoding="UTF- 8"?> 2 <?xml-stylesheet href="/css/atom. css" type="text/css"?> 3 <feed> 4 ... 5 <entry> 6 ... 7 </entry> 8 ... 9 </feed> |
其中
java代码: |
1 <?xml-stylesheet href="/css/atom. css" type="text/css"?> |
是与样式单相关的处理指令,指出本文档使用哪一个样式单来定义显示格式,如果用 CSS 来定义,type 就是 "text/css",用 XSLT 来定义,type 就是 "text/xsl"。
下面是其对应的 CSS 文件中的一段:
java代码: |
1 entry {
...}
2 display: block;
3 border: 2px solid black; 4 margin: 1em; 5 padding: 0.5em; 6 } |
可以看出,这段 CSS 与我们在 HTML 中使用的 CSS 没有什么区别。而且通过这段 CSS 可以想象出,浏览器在将在 entry 之外显示 2 个象素宽的边界。浏览器显示的结果与我们想象的完全相同(1.jpg)。
下面我来讲一讲 XSLT,因为在 XML 的世界里 XSLT 是比 CSS 更加重要的技术。现在最流行的 XML 文档的显示方法还是 XSLT。原因与 Schema 一样,XSLT 采用 XML 格式(CSS 采用自己的语法),可以用相同的 XML 解析器解析,便于程序处理。
XSLT 是参考 SGML 中的 DSSSL(定义 SGML 的显示格式)而设计的。最初叫做 XSL,但是后来 W3C 发现工作量实在太大就分成了两个规范:XSLT 和 XSL:FO。XSLT 很快就作为正式规范推出了,主要是面向转换类应用的;XSL:FO 主要是面向精确的格式定义(例如 PDF),正式规范才推出不久。我们主要用到 XSLT,因为 XSLT 目前已经达到了实用的阶段并且被广泛采用。XSLT 其实不完全是为显示目的设计的,XSLT 的主要作用是将 XML 由一种格式转换为另一种格式,例如由 XML 的一种词汇表转换为另一种词汇表,或者由 XML 转换为 HTML 或者 XHTML,便于在浏览器中显示。后一种应用(XML->HTML/XHTML)是目前 XSLT 应用最广的领域,被应用于很多符合 J2EE 标准的表示层框架中。这类框架在服务器端做 XSLT 转换,将生成的 HTML/XHTML 发给浏览器。基于这种技术的框架还可以非常方便地支持各种瘦客户端,例如 PDA 或具有无线上网功能的 WAP 手机(这时候是 XML->WML,也就是由一种词汇表转换为另一种词汇表,WML 本身就是 XML 的一种词汇表),无非就是另外再写一套 XSLT 样式单。在这种应用中,只包含数据的 XML 相当于 MVC 中的 Model,而表示显示格式的 XSLT 相当于 MVC 中的 View。还有一些需要在服务器定期生成静态页面的网站(例如新浪这类新闻网站)也在服务器采用 XML+XSLT 来定期生成静态页面(这类进程一般是以后台方式运行)。
现在 IE 和 Mozilla 两大浏览器都可以很好地支持 XSLT,所以从技术上讲可以完全采用 XML+XSLT 来制作所有的页面。在这里 XML+XSLT 与我们熟悉的 HTML+CSS 的功能大致相同。这也引起了另外的一种思考,究竟是在服务器端做 XSLT 转换好还是在浏览器端做 XSLT 转换好?我的考虑是尽管在服务器端做 XSLT 转换更加灵活,功能更加强大,我却更愿意在浏览器端做转换。因为 XSLT 转换是非常耗费资源的操作,如果在服务器端做大量的转换操作必然会极大地影响服务器的性能。必须要在服务器端做转换的话一个更好的方案是采用后台方式定期生成静态的 HTML 页面(也就是上面说的一些新闻网站所采取的做法),或者在服务器端通过软件实现某种 cache。这也符合我们一贯的思路:浏览器其实能做很多事情,而且现在主流机型(P4 2G、256M 以上内存)的处理能力已经非常强了,所以在浏览器力所能及的情况下应该把尽量多的工作交给浏览器去做。
就象刚才讲的 XML+CSS,我们来直观地看一下 XML+XSLT 是什么样子:
XML 文档 test.xml:
java代码: |
1 <?xml version=" 1. 0" encoding="gbk"?> 2 <?xml-stylesheet href="test. xsl" type="text/xsl"?> 3 <产品搜寻> 4 <摘要>搜寻字串:“滑鼠 键盘”,共找到 2 笔</摘要> 5 <产品> 6 <货号> 12478943</货号> 7 <品名>手不痛健康滑鼠</品名> 8 <定价>$ 234</定价> 9 <说明页 网址="http: //foo.bar/mouse/12478943">上市发表会</说明页> 10 </产品> 11 <产品> 12 <货号> 83424723</货号> 13 <品名>打不响静悄悄键盘</品名> 14 <定价>$ 567</定价> 15 <说明页 网址="http: //foo.bar/kbd/83424723">产品特性</说明页> 16 </产品> 17 </产品搜寻> |
XSLT 样式单 test.xsl:
java代码: |
1 <?xml version=" 1. 0" encoding="gbk"?> 2 <xsl:stylesheet version=" 1. 0" xmlns:xsl="http: //www.w3.org/1999/XSL/Transform"> 3 <xsl:output encoding="gbk"/> 4 5 <xsl:template match="/"> 6 <html> 7 <head> 8 <title>产品搜寻结果</title> 9 </head> 10 <body> 11 <h1>产品搜寻结果</h1> 12 <p><b>摘要:</b><xsl:value-of select="*/摘要"/></p> 13 <xsl:apply-templates select="产品搜寻"/> 14 </body> 15 </html> 16 </xsl:template> 17 18 <xsl:template match="产品搜寻"> 19 <table> 20 <tr> 21 <th>品名</th> 22 <th>定价</th> 23 <th>说明页</th> 24 </tr> 25 <xsl:for-each select="产品"> 26 <tr> 27 <td><xsl:value-of select="品名"/></td> 28 <td><xsl:value-of select="定价"/></td> 29 <td><a href=" {说明页/@网址 }"><xsl:value-of select="说明页"/></a></td> 30 </tr> 31 </xsl:for-each> 32 </table> 33 </xsl:template> 34 35 </xsl:stylesheet> |
这个例子是一个可以在 Mozilla 中正常显示的 XML 页面(2.jpg),从这个完整的例子中可以对我们刚才学到的知识产生很多感性认识。
a、XML 中的元素、属性名都可以使用中文,只要在 XML 声明中使用了正确的 encoding(这里是 gbk)。
b、在 XSLT 样式单中大量采用 XPath 语法来定位 DOM 中的节点并且对符合条件的节点进行转换。事实上 XPath 的产生最初就是因为 XSLT 的需要。
c、在 XSLT 样式单中同样可以使用名字空间。
d、XML 经 XSLT 转换后输出内容为标准的 HTML 页面,可以在浏览器中正常显示。
e、XSLT 转换是与有效性验证完全无关的操作,只要是满足格式正确的要求的 XML 就可以进行转换。
转换后生成的 HTML 其实是这样的:
java代码: |
1 <html> 2 <head> 3 <title>产品搜寻结果</title> 4 </head> 5 <body> 6 <h1>产品搜寻结果</h1> 7 <p><b>摘要:</b>搜寻字串:“滑鼠 键盘”,共找到 2 笔</p> 8 <table> 9 <tr> 10 <th>品名</th> 11 <th>定价</th> 12 <th>说明页</th> 13 </tr> 14 <tr> 15 <td>手不痛健康滑鼠</td> 16 <td>$ 234</td> 17 <td><a href="http: //foo.bar/kbd/83424723">上市发表会</a></td> 18 </tr> 19 <tr> 20 <td>打不响静悄悄键盘</td> 21 <td>$ 567</td> 22 <td><a href="http: //foo.bar/mouse/12478943">产品特性</a></td> 23 </tr> 24 </table> 25 </body> 26 </html> |
上面这个例子其实是来自于《无废话 XML》这本书。这个例子要在 IE 中正常显示只需要改一点点内容,这个任务留给你们课后去做。
关于 XSLT 首先应该看《无废话 XML》这本书,这本书里对 XSLT 的讲解是很清楚的。XSLT 我们在目前的开发中也用不到,但是 XSLT 在 XML 中是非常基础的知识,所以我在这一部分中用了较大的篇幅来讲述。
知道了这么多,你可能会认为 HTML+CSS 已经是是落后的技术,应该完全被 XML+XSLT 取代。这其实是一个错误的观念,连 W3C 都没有敢肯定 HTML+CSS 一定会被 XML+XSLT 取代。XML+XSLT 的主要缺点是成本和复杂性。由于缺乏所见即所得的(WYSIWYG)页面编辑器的支持,对于实现相同显示效果的复杂页面,XML+XSLT 的工作量要比 HTML+CSS 大得多。而且页面制作人员完全不能适应 XML+XSLT 的工作方式(这是程序员的工作方式),强求他们采用 XML+XSLT 是不现实的。目前 Dreamweaver 已经可以非常好地支持 CSS 了。所以在可预测的未来很长一段时间内,HTML+CSS 仍然是 Web 页面开发的主流技术。当然 HTML 将逐渐被 XHTML 所代替,但是这是另外的问题。XHTML 就是用 XML 来改造 HTML,使 HTML 至少符合 XML 格式正确的要求。符合 XHTML 标准的页面还有一些其它要求,感兴趣的可以看看《XML 高级编程》这本书。
以上这些就是我这次培训的所有内容。这些都是我认为的关于 XML 的基础知识,希望通过这次讲解以及课后的学习能够掌握好。掌握好了这些知识,再去掌握其它与 XML 相关的知识就是轻而易举的了。
相关资料:
1、《无废话 XML》
2、《W3C XML 规范》
3、《XML 高级编程》
4、《Java 与 XML》
5、《XML Schemas》
术语列表:
HTML:HyperText Markup Language,超文本标记语言
SGML:Standard Generalized Markup Language,标准通用标记语言。
XML:eXtensible Markup Language,可扩展标记语言。
DTD:Document Type Definition,文档类型定义。
XML Schema:XML 模式
RELAX NG:REgular LAnguage description for XML Next Generation,下一代的 RELAX。
DSSSL:Document Style Semantics and Specification Language,文档样式语义和规范语言
XSLT:eXtensible Stylesheet Language——Transformation,可扩展样式单语言转换部分
PI:Processing Instruction,处理指令。
XPath:XML 路径描述。
namespace:名字空间。
DOM:Document Object Model,文档对象模型。
SAX:Simple API for XML,XML 简单 API。
JAXP:Java API for XML Parsing
JDOM:Java Document Object Model
DOM4J:Document Object Model for Java
java代码: |
1 <?xml version=" 1. 0" encoding="gbk"?> 2 <DocRoot xmlns:xsi= 'http://www.w3.org/2001/XMLSchema-instance' xsi:schemaLocation="http: //www.xxx.com/dagl ./../mlzx/schemas/dz/wjnr.xsd" xmlns="http://www.xxx.com/dagl"> 3 ... 4 </DocRoot> |
我们可以看到在这个 XML 中声明了两个名字空间。声明名字空间使用 xmlns:前缀=URI 的形式,其中前缀也可以没有,用来设置 XML 文档缺省的名字空间。可以在任何元素上声明名字空间,声明后名字空间在该元素管辖范围(作用域)内起作用。在这个 XML 文档中使用了 Schema 来做有效性验证,因此必须声明 Schema 的名字空间。前缀可以是任意字符串(通常使用 xsi),但是这个名字空间所对应的 URI 是固定的,代表的是一个 Schema 的实例(Schema 与 XML 文档的关系就象是类和对象的关系一样)。名字空间声明后立即可以使用,所以在后面紧接着用这个名字空间前缀 xsi 设置了一个属性 schemaLocation,这个属性用来设置 Schema 文件的路径。xmlns="http://www.xxx.com/dagl" 中没有前缀设置了文档的缺省名字空间,也就是说在本文档中所有没有前缀的元素、属性都属于这个缺省的名字空间。
我们再来看看在 Schema 文件中使用名字空间的情况:
java代码: |
1 <?xml version=" 1. 0" encoding="gbk"?> 2 <schema xmlns="http: //www.w3.org/2001/XMLSchema" 3 xmlns:dagl="http: //www.xxx.com/dagl" 4 targetNamespace="http: //www.xxx.com/dagl" 5 elementFormDefault="qualified"> 6 7 <complexType name="attrType"> 8 <simpleContent> 9 <extension base="string"> 10 <attribute name="code_category" type="string" /> 11 <attribute name="id" type="string" /> 12 </extension> 13 </simpleContent> 14 </complexType> 15 16 <complexType name="dmType"> 17 <sequence> 18 ... 19 <element name="flh" type="dagl:attrType"/> 20 ... 21 </sequence> 22 </complexType> 23 ... 24 </schema> |
这个摘自刚才那个 XML 文档对应的 Schema。我们看到这个 Schema 中除了缺省的名字空间外还声明了一个前缀为 dagl 的名字空间,而且这个名字空间的 URI 必须与 targetNamespace 的 URI 相同。我把 Schema 规范设置为缺省名字空间是为了简化编写 Schema 定义,如果不这样做,那么每一个 complexType、sequence、element 都需要加上前缀(通常使用 xsd),那样是很麻烦的。targetNamespace 用来定义相应的 XML 文档(XML 实例,xsi)应该遵守的名字空间,也就是 XML 实例的缺省名字空间。为什么要这样做我不细讲了,这里的规则就是在 Schema 中自定义的类型(这里是 attrType)使用时必须加上名字空间前缀,这个前缀对应的名字空间 URI 必须与 targetNamespace 的 URI 相同。
现在我们对于 DTD、Schema、名字空间的作用都有了比较清晰的了解。在这一部分需要重点掌握的是名字空间的作用和使用方法。做 DTD、Schema 验证的具体方法可以看我发在 bbs 上的文章:介绍一下做 Schema 验证和 DTD 验证的方法
http://forum.hibernate.org.cn/viewtopic.php?t=859
用 Xerces 来做 DTD 和 Schema 验证是非常简单的。想要更详细地了解 DTD 和 Schema 的开发方法可以看《XML Schemas》和《XML 高级编程》这两本书。
因为性能上的原因,大部分情况下我们做 XML 开发时是不需要验证 XML 的有效性的。不过我们将来有可能会开发自己的 XML 词汇表,所以我们还是有必要学习相关知识的。我们将来主要会采用 Schema,需要重点学习这方面的知识,DTD 仅仅作为需要了解的知识。还有一个采用 Schema 而不采用 DTD 的重要原因是 DTD 不支持名字空间。由于 DTD 来自于 SGML,SGML 中完全没有名字空间的概念,因此 DTD 也完全不能识别 XML 中的名字空间。如果在 XML 中使用了包含了前缀的元素名,例如 <book:title>,用 DTD 做有效性验证时就会出错,DTD 顽固地认为你必须在相应 DTD 文件中定义一个 book:title 元素才行(它不会认为这个 book:title 其实就是一个 title)。而 Schema 完全支持名字空间,正是因为这个原因,而且考虑到名字空间的重要性,虽然目前 Schema 还存在缺陷,我们还是应该坚持使用 Schema 而不使用 DTD。
在目前阶段,只需要掌握好名字空间就可以了。Schema、DTD 等内容可以在需要的时候再去学习。
5、XML 与显示的结合
第 1 部分说到过,XML 的设计目标就是把内容与显示格式分离开。其实只有面向文档的应用才有显示的需求,面向数据的应用不需要定义显示格式。把内容与显示格式分离不仅有利于面向文档应用的开发,更有利于面向数据应用的开发。我来举个面向数据应用的例子,假设你做了一个 Web 搜索引擎(或者叫做网络爬虫),如果页面采用 HTML 来开发,当你的程序读到这样的信息:
java代码: |
1 <tr> 2 <td>XML 高级编程</td> 3 <td> 95. 00</td> 4 </tr> |
你的程序能辨别出这其实是一本书的名称和价格吗?尤其是当这些单纯表示格式的 <tr>、<td> 以不规则方式嵌套了很多层的时候,开发出一个支持 HTML 全文检索的搜索引擎简直是一场灾难。但是如果这些页面都是采用 XML 来开发的,并且内容与显示格式完全分离,当你的程度读到这样的信息:
java代码: |
1 <book> 2 <title>XML 高级编程</title> 3 <price> 95. 00</price> 4 </book> |
就可以非常容易地辨别出这是一本书的名称和价格,尤其是当这个 XML 页面使用了通用的词汇表并且通过了有效性验证时。所以 XML 就是搜索引擎开发者的福音,XML 还会催生出一大堆面向数据的应用,而这类应用在 XML 出现前是根本无法实现的。
言归正传,现在我们来谈谈面向文档的应用。那么显示格式用什么来定义呢?有两种方法,CSS 和 XSLT,XML 文档要在浏览器中显示必须结合 CSS 或者 XSLT 的样式单。XML 文档与 HTML 一样可以用 CSS 来定义显示格式(这里有个例子:http://www.wespoke.com/atom.xml)。
我们先看一下上面这个例子中如何用 CSS 来定义显示格式:
java代码: |
1 <?xml version=" 1. 0" encoding="UTF- 8"?> 2 <?xml-stylesheet href="/css/atom. css" type="text/css"?> 3 <feed> 4 ... 5 <entry> 6 ... 7 </entry> 8 ... 9 </feed> |
其中
java代码: |
1 <?xml-stylesheet href="/css/atom. css" type="text/css"?> |
是与样式单相关的处理指令,指出本文档使用哪一个样式单来定义显示格式,如果用 CSS 来定义,type 就是 "text/css",用 XSLT 来定义,type 就是 "text/xsl"。
下面是其对应的 CSS 文件中的一段:
java代码: |
1 entry {
...}
2 display: block;
3 border: 2px solid black; 4 margin: 1em; 5 padding: 0.5em; 6 } |
可以看出,这段 CSS 与我们在 HTML 中使用的 CSS 没有什么区别。而且通过这段 CSS 可以想象出,浏览器在将在 entry 之外显示 2 个象素宽的边界。浏览器显示的结果与我们想象的完全相同(1.jpg)。
下面我来讲一讲 XSLT,因为在 XML 的世界里 XSLT 是比 CSS 更加重要的技术。现在最流行的 XML 文档的显示方法还是 XSLT。原因与 Schema 一样,XSLT 采用 XML 格式(CSS 采用自己的语法),可以用相同的 XML 解析器解析,便于程序处理。
XSLT 是参考 SGML 中的 DSSSL(定义 SGML 的显示格式)而设计的。最初叫做 XSL,但是后来 W3C 发现工作量实在太大就分成了两个规范:XSLT 和 XSL:FO。XSLT 很快就作为正式规范推出了,主要是面向转换类应用的;XSL:FO 主要是面向精确的格式定义(例如 PDF),正式规范才推出不久。我们主要用到 XSLT,因为 XSLT 目前已经达到了实用的阶段并且被广泛采用。XSLT 其实不完全是为显示目的设计的,XSLT 的主要作用是将 XML 由一种格式转换为另一种格式,例如由 XML 的一种词汇表转换为另一种词汇表,或者由 XML 转换为 HTML 或者 XHTML,便于在浏览器中显示。后一种应用(XML->HTML/XHTML)是目前 XSLT 应用最广的领域,被应用于很多符合 J2EE 标准的表示层框架中。这类框架在服务器端做 XSLT 转换,将生成的 HTML/XHTML 发给浏览器。基于这种技术的框架还可以非常方便地支持各种瘦客户端,例如 PDA 或具有无线上网功能的 WAP 手机(这时候是 XML->WML,也就是由一种词汇表转换为另一种词汇表,WML 本身就是 XML 的一种词汇表),无非就是另外再写一套 XSLT 样式单。在这种应用中,只包含数据的 XML 相当于 MVC 中的 Model,而表示显示格式的 XSLT 相当于 MVC 中的 View。还有一些需要在服务器定期生成静态页面的网站(例如新浪这类新闻网站)也在服务器采用 XML+XSLT 来定期生成静态页面(这类进程一般是以后台方式运行)。
现在 IE 和 Mozilla 两大浏览器都可以很好地支持 XSLT,所以从技术上讲可以完全采用 XML+XSLT 来制作所有的页面。在这里 XML+XSLT 与我们熟悉的 HTML+CSS 的功能大致相同。这也引起了另外的一种思考,究竟是在服务器端做 XSLT 转换好还是在浏览器端做 XSLT 转换好?我的考虑是尽管在服务器端做 XSLT 转换更加灵活,功能更加强大,我却更愿意在浏览器端做转换。因为 XSLT 转换是非常耗费资源的操作,如果在服务器端做大量的转换操作必然会极大地影响服务器的性能。必须要在服务器端做转换的话一个更好的方案是采用后台方式定期生成静态的 HTML 页面(也就是上面说的一些新闻网站所采取的做法),或者在服务器端通过软件实现某种 cache。这也符合我们一贯的思路:浏览器其实能做很多事情,而且现在主流机型(P4 2G、256M 以上内存)的处理能力已经非常强了,所以在浏览器力所能及的情况下应该把尽量多的工作交给浏览器去做。
就象刚才讲的 XML+CSS,我们来直观地看一下 XML+XSLT 是什么样子:
XML 文档 test.xml:
java代码: |
1 <?xml version=" 1. 0" encoding="gbk"?> 2 <?xml-stylesheet href="test. xsl" type="text/xsl"?> 3 <产品搜寻> 4 <摘要>搜寻字串:“滑鼠 键盘”,共找到 2 笔</摘要> 5 <产品> 6 <货号> 12478943</货号> 7 <品名>手不痛健康滑鼠</品名> 8 <定价>$ 234</定价> 9 <说明页 网址="http: //foo.bar/mouse/12478943">上市发表会</说明页> 10 </产品> 11 <产品> 12 <货号> 83424723</货号> 13 <品名>打不响静悄悄键盘</品名> 14 <定价>$ 567</定价> 15 <说明页 网址="http: //foo.bar/kbd/83424723">产品特性</说明页> 16 </产品> 17 </产品搜寻> |
XSLT 样式单 test.xsl:
java代码: |
1 <?xml version=" 1. 0" encoding="gbk"?> 2 <xsl:stylesheet version=" 1. 0" xmlns:xsl="http: //www.w3.org/1999/XSL/Transform"> 3 <xsl:output encoding="gbk"/> 4 5 <xsl:template match="/"> 6 <html> 7 <head> 8 <title>产品搜寻结果</title> 9 </head> 10 <body> 11 <h1>产品搜寻结果</h1> 12 <p><b>摘要:</b><xsl:value-of select="*/摘要"/></p> 13 <xsl:apply-templates select="产品搜寻"/> 14 </body> 15 </html> 16 </xsl:template> 17 18 <xsl:template match="产品搜寻"> 19 <table> 20 <tr> 21 <th>品名</th> 22 <th>定价</th> 23 <th>说明页</th> 24 </tr> 25 <xsl:for-each select="产品"> 26 <tr> 27 <td><xsl:value-of select="品名"/></td> 28 <td><xsl:value-of select="定价"/></td> 29 <td><a href=" {说明页/@网址 }"><xsl:value-of select="说明页"/></a></td> 30 </tr> 31 </xsl:for-each> 32 </table> 33 </xsl:template> 34 35 </xsl:stylesheet> |
这个例子是一个可以在 Mozilla 中正常显示的 XML 页面(2.jpg),从这个完整的例子中可以对我们刚才学到的知识产生很多感性认识。
a、XML 中的元素、属性名都可以使用中文,只要在 XML 声明中使用了正确的 encoding(这里是 gbk)。
b、在 XSLT 样式单中大量采用 XPath 语法来定位 DOM 中的节点并且对符合条件的节点进行转换。事实上 XPath 的产生最初就是因为 XSLT 的需要。
c、在 XSLT 样式单中同样可以使用名字空间。
d、XML 经 XSLT 转换后输出内容为标准的 HTML 页面,可以在浏览器中正常显示。
e、XSLT 转换是与有效性验证完全无关的操作,只要是满足格式正确的要求的 XML 就可以进行转换。
转换后生成的 HTML 其实是这样的:
java代码: |
1 <html> 2 <head> 3 <title>产品搜寻结果</title> 4 </head> 5 <body> 6 <h1>产品搜寻结果</h1> 7 <p><b>摘要:</b>搜寻字串:“滑鼠 键盘”,共找到 2 笔</p> 8 <table> 9 <tr> 10 <th>品名</th> 11 <th>定价</th> 12 <th>说明页</th> 13 </tr> 14 <tr> 15 <td>手不痛健康滑鼠</td> 16 <td>$ 234</td> 17 <td><a href="http: //foo.bar/kbd/83424723">上市发表会</a></td> 18 </tr> 19 <tr> 20 <td>打不响静悄悄键盘</td> 21 <td>$ 567</td> 22 <td><a href="http: //foo.bar/mouse/12478943">产品特性</a></td> 23 </tr> 24 </table> 25 </body> 26 </html> |
上面这个例子其实是来自于《无废话 XML》这本书。这个例子要在 IE 中正常显示只需要改一点点内容,这个任务留给你们课后去做。
关于 XSLT 首先应该看《无废话 XML》这本书,这本书里对 XSLT 的讲解是很清楚的。XSLT 我们在目前的开发中也用不到,但是 XSLT 在 XML 中是非常基础的知识,所以我在这一部分中用了较大的篇幅来讲述。
知道了这么多,你可能会认为 HTML+CSS 已经是是落后的技术,应该完全被 XML+XSLT 取代。这其实是一个错误的观念,连 W3C 都没有敢肯定 HTML+CSS 一定会被 XML+XSLT 取代。XML+XSLT 的主要缺点是成本和复杂性。由于缺乏所见即所得的(WYSIWYG)页面编辑器的支持,对于实现相同显示效果的复杂页面,XML+XSLT 的工作量要比 HTML+CSS 大得多。而且页面制作人员完全不能适应 XML+XSLT 的工作方式(这是程序员的工作方式),强求他们采用 XML+XSLT 是不现实的。目前 Dreamweaver 已经可以非常好地支持 CSS 了。所以在可预测的未来很长一段时间内,HTML+CSS 仍然是 Web 页面开发的主流技术。当然 HTML 将逐渐被 XHTML 所代替,但是这是另外的问题。XHTML 就是用 XML 来改造 HTML,使 HTML 至少符合 XML 格式正确的要求。符合 XHTML 标准的页面还有一些其它要求,感兴趣的可以看看《XML 高级编程》这本书。
以上这些就是我这次培训的所有内容。这些都是我认为的关于 XML 的基础知识,希望通过这次讲解以及课后的学习能够掌握好。掌握好了这些知识,再去掌握其它与 XML 相关的知识就是轻而易举的了。
相关资料:
1、《无废话 XML》
2、《W3C XML 规范》
3、《XML 高级编程》
4、《Java 与 XML》
5、《XML Schemas》
术语列表:
HTML:HyperText Markup Language,超文本标记语言
SGML:Standard Generalized Markup Language,标准通用标记语言。
XML:eXtensible Markup Language,可扩展标记语言。
DTD:Document Type Definition,文档类型定义。
XML Schema:XML 模式
RELAX NG:REgular LAnguage description for XML Next Generation,下一代的 RELAX。
DSSSL:Document Style Semantics and Specification Language,文档样式语义和规范语言
XSLT:eXtensible Stylesheet Language——Transformation,可扩展样式单语言转换部分
PI:Processing Instruction,处理指令。
XPath:XML 路径描述。
namespace:名字空间。
DOM:Document Object Model,文档对象模型。
SAX:Simple API for XML,XML 简单 API。
JAXP:Java API for XML Parsing
JDOM:Java Document Object Model
DOM4J:Document Object Model for Java
java代码: |
1 List list = document. selectNodes ( //foo/bar ); 2 Node node = document. selectSingleNode ( //foo/bar/author); 3 |
这与我们前台写的 JavaScript 是非常相似的,我们学会 DOM4J 是几乎不需要花什么时间。
DOM4J 与 JDOM 一样,通过 SAX 或 DOM(一般用 SAX)读入 XML 中的信息在内存中生成自己的数据结构,因此 DOM4J 至少需要一个实现了 SAX 的 XML 解析器,我们可以直接使用 Xerces。
由于 DOM4J 具有易用性、性能、灵活性、功能强大等多方面的优势,今后我们如果需要在服务器端做 XML 开发,DOM4J 将是我们主要采用的工具。
关于这几种 API 的详细内容,请参考《Java 与 XML》和《XML 高级编程》。关于 DOM4J,主要有这些资料:
http://dom4j.sourceforge.net/faq.html
http://www.csdn.net/develop/article/22/22753.shtm
听了我上面的介绍你们可能会觉得 DOM 是比 SAX 更基础的 API,因为它是 W3C 的标准,所有的语言都支持,而 SAX 的使用仅局限于少数几种语言。某种程度上你是对的,但是在 Java 的世界里,SAX 是比 DOM 更加基础的 API。由于 SAX 处理效率很高,SAX 的应用范围比 DOM 更广。例如:我们中间件的低层框架 Avalon(也是 Apache 软件基金会的产品)处理 XML 配置文件时使用的就是 SAX。另外,因为 W3C 并没有规定在内存中如何生成一棵 DOM 树(W3C 只规定了如何操作这棵 DOM 树),Xerces 采用高效率的 SAX 来读入 XML,然后生成 DOM 树。因此当你在用 Xerces 做 DOM 开发时发现经常需要捕获 SAXException 就没什么可奇怪的了。JDOM 和 DOM4J 通常也是使用 SAX 读入 XML,然后生成自己的数据结构。对于 JAXP、JDOM 和 DOM4J 来说,DOM 和 SAX 都是基础的 API。
在我们公司做开发最常接触的 XML 开发是使用 JavaScript 做 DOM 开发,因为时间有限,所以我今天只详细讲一下在前台使用 JavaScript 和 DOM API 做 XML 开发的过程。今天的目的主要是让大家对于 XML 相关的知识有一个整体的了解。
大家知道 JavaScript 是嵌入在浏览器中的,创建 DOM 树不是 JavaScript 的责任,浏览器已经创建好了,另外通常 JavaScript 也不能直接读写 XML 文件(在权限许可的情况下 JavaScript 可以读写本地文件)。JavaScript 处理的 XML 数据有两个来源,一个是来自于页面中的 XML 数据岛,另外一个是来自于从 XMLHTTP 接口接收的后台发来的 XML 数据。注意:在浏览器中的 DOM 有两种,HTML DOM 和 XML DOM。如何处理 HTML DOM 在普通的 JavaScript 教材(《JavaScript 权威指南》等等)中已经讲得很详细了,我这里只详细讲一下如何处理 XML DOM,下面所说的 DOM 都是指 XML DOM。在讲 XML DOM 之前我首先要讲一下 XPath。
什么是 XPath?简单地说,XPath 就是定位 XML 中某些节点(元素、属性、内容、处理指令、文档类型定义、注释)的方法。XPath 的目的是为 XSLT 和 XPointer 提供一个共同的、整合的语法,用来对应 XML 的各个部分,选择 XML 中的某个或某些节点。XPath 是在 DOM 树中查找节点、做 XSLT 转换、定义文档内部指针(XPointer)的基础。有时候也把一个符合 XPath 规范的表达式称做一个 xpath。我们通常把 XPath 表达式的结果称为一个节点集(node set)。节点集能够被转换、复制、忽略或执行其它的合法操作。XPath 除了包括定位语法外还包括很多函数定义,这些函数分成 4 类:节点集函数、字符串函数、布尔函数和数值函数。节点集函数,例如 position() 和 count(),通常以节点集作为输入(以 XPath 表达式的形式),然后做进一步处理。其它 3 种类型的函数,例如 substring()、not() 和 round() 提供基本的字符串处理、逻辑运算和数值运算的功能。关于 XPath 中各种函数定义的详细内容可以参考《XML 高级编程》这本书。所有这些表达式语法或函数定义都是 XPath 规范或实现的一部分。
好了,长话短说,我在这里主要讲一下 XPath 如何使用。其实我们需要知道的基本上就是《无废话 XML》中表 7.1 的内容。
XPath 的定位语法与操作系统目录结构的语法非常相似。也分成两种,相对路径与绝对路径。相对路径从当前正在处理的节点开始,绝对路径从文档的根节点开始。我来举些例子:
java代码: |
1 A 对应当前节点下所有名为 A 的子元素。 2 * 对应当前节点下所有子元素。 3 */A 对应自当前节点开始,所有名为 A 的孙元素。 4 @A 对应一个附属于当前节点,名为 A 的属性。 5 @* 对应所有附属于当前节点的属性。 6 text ( ) 对应当前节点的子元素中的所有文本节点。 7 . 对应当前节点 8 .. 对应当前节点的父节点。 9 A [ 1 ] 对应当前节点下,第一个名为 A 的子元素。 10 A [position ( )= 1 ] 作用同上。 11 A/ [@B=" true" ] 对应当前节点下所有名为 A 的子元素,这个子元素必须含有一个名为 B 的属性,其属性?当匦?为 " true"。 12 A|B 对应当前节点下,所有名为 A 或 B 的子元素;| 代表“或”的关系。 13 . //A 对应当前节点下,所有名为 A 的元素;// 符号代表可跨?绞?。 14 A //B 对应到所有名为 B 的元素,它们的上级(可跨?绞叮?必须有一个名为 A 的元素,而且 A 元素必须是当前节点的子元素。 15 /A 对应根节点下所有名为 A 的子元素。 16 //A 对应根节点下所有名为 A 的元素。A 元素可以在任意层次。 17 |
我们在做前台开发时,有两个方法会用到 XPath,selectSingleNode() 和 selectNodes()。这两个方法是 IE 的扩展,不属于 W3C DOM 规范,但是使用起来非常方便,所以我们在 DOM4J 中也可以看到这两个方法。
java代码: |
1 hwn.__drTags = sr1. selectSingleNode ("./tag" ); 2 var cxn = xn. selectNodes (". /*[not(@m:f) or @m:f!='d']"); |
上面是从我们的前台开发框架中 copy 出来的代码。
selectSingleNode 用于获得一个节点,如果有多个节点满足条件,返回的是第一个节点。
selectNodes 用于获得节点集,返回的结果是包含所有满足条件节点的数组。
第二条语句的参数看起来有些复杂,它的意思是找到 xn 节点下所有不包含 m:f 属性或者包含 m:f 属性,但属性值不等于 'd' 的所有子元素。
下面我来详细讲解 XML DOM 在 JavaScript 中的实现。
在浏览器中,无论 XML 的数据来源如何,最后都会由浏览器生成一棵 DOM 树。这棵 DOM 树所对应的对象类型为 XMLDocument,这棵 DOM 树上的所有节点(包括根节点)都是 XMLNodes 类型的对象。事实上 XMLDocument 也是 XMLNodes 的子类,所以你可以在任何一种 XMLNodes 派生出的对象上调用 selectSingleNode 和 selectNodes。
数据来自 XML 数据岛:
例如在页面中有这样一个 XML 数据岛,
<xml id="book">
...
</xml>
var xd = book.XMLDocument;
通过外部 XML 文件创建新的 DOM 树:
var xd = new ActiveXObject("Microsoft.XMLDOM");
xd.load(data.xml);
或者将外部文件中的数据加载到 XML 数据岛:
var xd = book.XMLDocument;
xd.load(data.xml);
注意:load() 这个方法的参数可以是任意合法的 URL,不一定限制为文件名。
将数据保存到 XML 文件用
xd.save(data.xml);
通过字符串创建新的 DOM 树:
var xd = new ActiveXObject("Microsoft.XMLDOM");
xd.loadXML("<"+hd.__hwnodes.style.rootTag+"></"+hd.__hwnodes.style.rootTag+">");
数据来自 XMLHTTP:
var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
xmlhttp.open("GET","/hwsvr2/qe",false);
xmlhttp.send(null);
var xd = xmlhttp.responseXML;
得到 XML 文档的根节点:
var xn = xd.documentElement;
我再来说说在 Mozilla 中创建 XML DOM 树的方法,将来我们有可能会为 Mozilla 开发页面,所以对于如何保持脚本的兼容性有些了解是有好处的,这部分内容不是必须掌握的内容。
Mozilla 中并没有 IE 的 XML 数据岛这个概念,不过在 Mozilla 中模拟 XML 数据岛是非常容易的,这里有这方面的资料:
http://www.mozilla.org/xmlextras/xmldataislands/
感兴趣的可以看看。
Mozilla 中内建有对 XML DOM 的支持,并没有使用 ActiveX 之类外挂的方式(被 Mozilla 开发者讥笑为打补丁的方式)。在 Mozilla 中访问创建好的 DOM 树的方法与 IE 中基本上是一样的,所不同的是创建 DOM 的过程,Mozilla 采用的方法都是符合 W3C 标准的方法:
通过外部 XML 文件创建新的 DOM 树:
var xd= document.implementation.createDocument("","",null);
xd.load("data.xml");
第一个参数是名字空间的 URL,第二个参数是 DOM 根元素名称,第三个参数是定义 XML 格式的 DTD。在这里这 3 个参数都可以不提供。
Mozilla 中没有 loadXML() 这个简单的方法,但是可以手工为 Mozilla 添加这个方法,详细信息在这里:
http://webfx.eae.net/dhtml/xmlextras/xmlextras.html
数据来自 XMLHTTP:
var xmlhttp=new XMLHttpRequest();
xmlhttp.open("GET","/hwsvr2/qe",false);
xmlhttp.send(null);
var xd = xmlhttp.responseXML;
得到 XML 文档的根节点:
var xn = xd.documentElement;
由上面可以看到只要创建了 XML DOM 树后,访问方法 IE、Mozilla 是完全一样的,这部分的差异非常容易通过封装的方法屏蔽掉。Mozilla 没有提供 selectSingleNode() 和 selectNodes() 两个方法,以前我写过这两个方法在 Mozilla 上的实现,贴在论坛上,感兴趣的可以看看:
http://forum.hibernate.org.cn/viewtopic.php?t=965
下面我来讲讲访问 DOM 树常用的方法。
刚才我们说到过,DOM 树的访问有两个级别,文档级别和节点级别。
在文档级别,XMLDocument 对象上有这些方法:
abort():终止正在进行的异步下载
getElementByTagName():根据标记名称查找当前节点的子元素。
load():从指定位置下载 XML。
save():将 XML 保存到指定位置。
XMLDocument 对象上有这些属性:
async:指出 XML 是否可以异步下载。
doctype:返回 XML 的 DTD 节点。
documentElement:返回 XML 的根元素。
parseError:返回包含 parse error 信息的对象。
在节点级别,XMLNodes 对象上有这些方法:
createElement():在当前节点下创建一个子元素。
createTextNode():在当前节点下创建一个内容节点。
appendChild():在当前节点上添加新的子节点。
cloneNode():复制一个节点,返回值为生成的新节点。
hasChildNodes():当前节点是否为叶节点。
insertBefore():在某节点前插入新的节点。
removeChild():删除当前节点的子节点。
replaceChild():替换当前节点的子节点。
XMLNodes 对象上有这些属性:
childNodes:包含所有的子节点数组。
firstChild:当前节点的第一个子节点
lastChild:当前节点的最后一个子节点
nextSibling:当前节点的下一个兄弟节点
nodeValue:节点值
ownerDocument:当前节点对应的 XML 文档
parentNode:当前节点的父节点。
parentElement:父元素的句柄。
xml:从当前节点开始子树所生成的 XML。这个属性只有 IE 支持。
对于所有的元素节点,可以使用这两个方法:
setAttribute():设置当前元素的属性。
getAttribute():获得当前元素的属性。
clearAttributes():清除当前元素的所有属性。
另外还有 selectSingleNode() 和 selectNodes(),这两个方法其实是最常用的了。由于 XML DOM 中可以使用强大的 XPath 查找你想要查找的任意节点,比 HTML DOM 中仅能用 all()、getElementById()、getElementByTagName() 几个功能有限的方法方便得多。
只要你理解了 DOM 的概念,并且没有忘记以前学的数据结构的知识,上面的这些方法理解起来是非常简单的,我就不细讲了,关键是要经常使用,熟能生巧。
4、XML 有效性验证的方法和名字空间
符合第 2 部分讲的 4 个条件的 XML 叫做格式正确的(well-formed)XML。还有一种有效的(valid)XML。什么叫做有效的 XML 呢?
有效的 XML 就是通过某种格式定义来规定这个 XML 中只能有哪些元素、这些元素应该按照什么顺序出现,每个元素有哪些属性,这些属性的数据类型、取值范围等信息。然后这一类的 XML 只要通过验证符合这个格式定义就认为是有效的 XML。那么为什么 XML 需要通过验证,这主要是电子商务的需要。举个例子,如果你需要通过 Web 方式与客户进行 B2B 交易,你把电子形式的发票发给客户,客户也会把他们的发票发给你,但是两种发票的格式不同,就好象你在说汉语他在说英语,彼此无法正常交流,所以必须要有一个统一的格式(标准)才能够开展正常的电子商务活动。XML 就是靠不同类型的格式定义来建造不同的标记语言的,每一种格式定义(标记语言)叫做一种词汇表(vocabulary)。
XML 的格式定义有很多种方法,包括 DTD、XML Schema、RELAX NG 等等。DTD 是参考 SGML DTD 创造出的 XML 格式定义方法。DTD 的格式定义采用与 XML 不同的语法,这使得很难直接用解析器来解析 DTD,也很难动态(runtime)验证 XML 的有效性。W3C 后来又创造了 XML Schema。Schema 是一种新型的 XML 格式定义方法,它完全采用 XML 语法,便于解析器处理,而且对于数据格式的定义更加严格和精确,所以 Schema 更加适合面向数据的应用。现在的 XML 解析器一般都支持做 DTD 验证,也有同时支持做 DTD 和 Schema 验证的,例如我们使用的 Xerces。那么有了 Schema 是否我们就可以完全抛弃 DTD 呢?答案是否定的,由于 DTD 来自于 SGML,它非常适合面向文档的应用(SGML 完全是为文档处理而设计的)。定义相同的 XML 格式,DTD 定义要比 Schema 简练的多,Schema 定义则显得很冗长,所以 DTD 更加适合面向文档的应用,不过把 Schema 用于面向文档的应用也不会有多大的问题。因为 Schema 的格式定义很烦琐,所有有人开发出了其它的格式定义方法,其中比较有前途的是 RELAX NG。RELAX NG 是一种以 RELAX(由日本人开发)与 TREX(由 XML 界的权威 James Clark 开发)为基础的模式语言。它的基本思想与 Schema 相同,也采用 XML 格式,所以程序处理起来也很方便,而且它的语法比 Schema 要简单的多。但是目前 RELAX NG 还不是 W3C 的标准,所以大多数解析器都不支持(需要使用专门的软件包)。我的看法是 Schema 可能会在今后参考 RELAX NG 而得到简化,所以我们还是应该更多地使用 Schema。
现在我来讲一下名字空间这个非常重要的概念。名字空间(namespace)在 XML 中的作用其实与 package 在 Java 中的作用非常象。XML 是一种元语言,可以建造出无穷多种标记语言。有些时候需要在同一个 XML 文档中混用不同类型的标记语言,但是这些不同类型的标记语言中可能有重复的标记,例如:我为书籍分类建造了一种标记语言,其中有 <title> 这个标记;我又为影片分类建造了一种标记语言,其中也有 <title> 这个标记。但是在我的 XML 文档中需要同时包含书籍和影片两类的数据(假设这个 XML 文档其实是用户的一个购物车,他同时买了书和 DVD),我如何区分一个 <title> 究竟是书籍的 title 还是影片的 title 呢?这时候名字空间就可以帮你了。名字空间就是一个元素前缀与 URI 之间的一种映射关系。名字空间是用一个 XML 元素加上一个前缀组成的,比如 <book:title> 和 <picture:title>。这样 XML 解析器可以在不使用不同的元素名字的情况下,区分上述两个元素名字。元素前缀可以为任意字符串,最终一个 XML 文档中的每个元素前缀要映射到一个唯一的 URI,这个 URI 就是不同的 XML 词汇表的标识。除了用于元素,名字空间同样也可以用于属性(下面我们马上可以看到)。名字空间经常在 XML 文档中使用,也可以在 Schema 及 XSLT 样式单或者 XML 有关的规范中使用。下面我们看一下名字空间的具体使用方法:
java代码: |
1 <?xml version=" 1. 0" encoding="gbk"?> 2 <DocRoot xmlns:xsi= 'http://www.w3.org/2001/XMLSchema-instance' xsi:schemaLocation="http: //www.xxx.com/dagl ./../mlzx/schemas/dz/wjnr.xsd" xmlns="http://www.xxx.com/dagl"> 3 ... 4 </DocRoot> |
我们可以看到在这个 XML 中声明了两个名字空间。声明名字空间使用 xmlns:前缀=URI 的形式,其中前缀也可以没有,用来设置 XML 文档缺省的名字空间。可以在任何元素上声明名字空间,声明后名字空间在该元素管辖范围(作用域)内起作用。在这个 XML 文档中使用了 Schema 来做有效性验证,因此必须声明 Schema 的名字空间。前缀可以是任意字符串(通常使用 xsi),但是这个名字空间所对应的 URI 是固定的,代表的是一个 Schema 的实例(Schema 与 XML 文档的关系就象是类和对象的关系一样)。名字空间声明后立即可以使用,所以在后面紧接着用这个名字空间前缀 xsi 设置了一个属性 schemaLocation,这个属性用来设置 Schema 文件的路径。xmlns="http://www.xxx.com/dagl" 中没有前缀设置了文档的缺省名字空间,也就是说在本文档中所有没有前缀的元素、属性都属于这个缺省的名字空间。
我们再来看看在 Schema 文件中使用名字空间的情况:
java代码: |
1 <?xml version=" 1. 0" encoding="gbk"?> 2 <schema xmlns="http: //www.w3.org/2001/XMLSchema" 3 xmlns:dagl="http: //www.xxx.com/dagl" 4 targetNamespace="http: //www.xxx.com/dagl" 5 elementFormDefault="qualified"> 6 7 <complexType name="attrType"> 8 <simpleContent> 9 <extension base="string"> 10 <attribute name="code_category" type="string" /> 11 <attribute name="id" type="string" /> 12 </extension> 13 </simpleContent> 14 </complexType> 15 16 <complexType name="dmType"> 17 <sequence> 18 ... 19 <element name="flh" type="dagl:attrType"/> 20 ... 21 </sequence> 22 </complexType> 23 ... 24 </schema> |
这个摘自刚才那个 XML 文档对应的 Schema。我们看到这个 Schema 中除了缺省的名字空间外还声明了一个前缀为 dagl 的名字空间,而且这个名字空间的 URI 必须与 targetNamespace 的 URI 相同。我把 Schema 规范设置为缺省名字空间是为了简化编写 Schema 定义,如果不这样做,那么每一个 complexType、sequence、element 都需要加上前缀(通常使用 xsd),那样是很麻烦的。targetNamespace 用来定义相应的 XML 文档(XML 实例,xsi)应该遵守的名字空间,也就是 XML 实例的缺省名字空间。为什么要这样做我不细讲了,这里的规则就是在 Schema 中自定义的类型(这里是 attrType)使用时必须加上名字空间前缀,这个前缀对应的名字空间 URI 必须与 targetNamespace 的 URI 相同。
现在我们对于 DTD、Schema、名字空间的作用都有了比较清晰的了解。在这一部分需要重点掌握的是名字空间的作用和使用方法。做 DTD、Schema 验证的具体方法可以看我发在 bbs 上的文章:介绍一下做 Schema 验证和 DTD 验证的方法
http://forum.hibernate.org.cn/viewtopic.php?t=859
用 Xerces 来做 DTD 和 Schema 验证是非常简单的。想要更详细地了解 DTD 和 Schema 的开发方法可以看《XML Schemas》和《XML 高级编程》这两本书。
因为性能上的原因,大部分情况下我们做 XML 开发时是不需要验证 XML 的有效性的。不过我们将来有可能会开发自己的 XML 词汇表,所以我们还是有必要学习相关知识的。我们将来主要会采用 Schema,需要重点学习这方面的知识,DTD 仅仅作为需要了解的知识。还有一个采用 Schema 而不采用 DTD 的重要原因是 DTD 不支持名字空间。由于 DTD 来自于 SGML,SGML 中完全没有名字空间的概念,因此 DTD 也完全不能识别 XML 中的名字空间。如果在 XML 中使用了包含了前缀的元素名,例如 <book:title>,用 DTD 做有效性验证时就会出错,DTD 顽固地认为你必须在相应 DTD 文件中定义一个 book:title 元素才行(它不会认为这个 book:title 其实就是一个 title)。而 Schema 完全支持名字空间,正是因为这个原因,而且考虑到名字空间的重要性,虽然目前 Schema 还存在缺陷,我们还是应该坚持使用 Schema 而不使用 DTD。
在目前阶段,只需要掌握好名字空间就可以了。Schema、DTD 等内容可以在需要的时候再去学习。
5、XML 与显示的结合
第 1 部分说到过,XML 的设计目标就是把内容与显示格式分离开。其实只有面向文档的应用才有显示的需求,面向数据的应用不需要定义显示格式。把内容与显示格式分离不仅有利于面向文档应用的开发,更有利于面向数据应用的开发。我来举个面向数据应用的例子,假设你做了一个 Web 搜索引擎(或者叫做网络爬虫),如果页面采用 HTML 来开发,当你的程序读到这样的信息:
java代码: |
1 <tr> 2 <td>XML 高级编程</td> 3 <td> 95. 00</td> 4 </tr> |
你的程序能辨别出这其实是一本书的名称和价格吗?尤其是当这些单纯表示格式的 <tr>、<td> 以不规则方式嵌套了很多层的时候,开发出一个支持 HTML 全文检索的搜索引擎简直是一场灾难。但是如果这些页面都是采用 XML 来开发的,并且内容与显示格式完全分离,当你的程度读到这样的信息:
java代码: |
1 <book> 2 <title>XML 高级编程</title> 3 <price> 95. 00</price> 4 </book> |
就可以非常容易地辨别出这是一本书的名称和价格,尤其是当这个 XML 页面使用了通用的词汇表并且通过了有效性验证时。所以 XML 就是搜索引擎开发者的福音,XML 还会催生出一大堆面向数据的应用,而这类应用在 XML 出现前是根本无法实现的。
言归正传,现在我们来谈谈面向文档的应用。那么显示格式用什么来定义呢?有两种方法,CSS 和 XSLT,XML 文档要在浏览器中显示必须结合 CSS 或者 XSLT 的样式单。XML 文档与 HTML 一样可以用 CSS 来定义显示格式(这里有个例子:http://www.wespoke.com/atom.xml)。
我们先看一下上面这个例子中如何用 CSS 来定义显示格式:
java代码: |
1 <?xml version=" 1. 0" encoding="UTF- 8"?> 2 <?xml-stylesheet href="/css/atom. css" type="text/css"?> 3 <feed> 4 ... 5 <entry> 6 ... 7 </entry> 8 ... 9 </feed> |
其中
java代码: |
1 <?xml-stylesheet href="/css/atom. css" type="text/css"?> |
是与样式单相关的处理指令,指出本文档使用哪一个样式单来定义显示格式,如果用 CSS 来定义,type 就是 "text/css",用 XSLT 来定义,type 就是 "text/xsl"。
下面是其对应的 CSS 文件中的一段:
java代码: |
1 entry {
...}
2 display: block;
3 border: 2px solid black; 4 margin: 1em; 5 padding: 0.5em; 6 } |
可以看出,这段 CSS 与我们在 HTML 中使用的 CSS 没有什么区别。而且通过这段 CSS 可以想象出,浏览器在将在 entry 之外显示 2 个象素宽的边界。浏览器显示的结果与我们想象的完全相同(1.jpg)。
下面我来讲一讲 XSLT,因为在 XML 的世界里 XSLT 是比 CSS 更加重要的技术。现在最流行的 XML 文档的显示方法还是 XSLT。原因与 Schema 一样,XSLT 采用 XML 格式(CSS 采用自己的语法),可以用相同的 XML 解析器解析,便于程序处理。
XSLT 是参考 SGML 中的 DSSSL(定义 SGML 的显示格式)而设计的。最初叫做 XSL,但是后来 W3C 发现工作量实在太大就分成了两个规范:XSLT 和 XSL:FO。XSLT 很快就作为正式规范推出了,主要是面向转换类应用的;XSL:FO 主要是面向精确的格式定义(例如 PDF),正式规范才推出不久。我们主要用到 XSLT,因为 XSLT 目前已经达到了实用的阶段并且被广泛采用。XSLT 其实不完全是为显示目的设计的,XSLT 的主要作用是将 XML 由一种格式转换为另一种格式,例如由 XML 的一种词汇表转换为另一种词汇表,或者由 XML 转换为 HTML 或者 XHTML,便于在浏览器中显示。后一种应用(XML->HTML/XHTML)是目前 XSLT 应用最广的领域,被应用于很多符合 J2EE 标准的表示层框架中。这类框架在服务器端做 XSLT 转换,将生成的 HTML/XHTML 发给浏览器。基于这种技术的框架还可以非常方便地支持各种瘦客户端,例如 PDA 或具有无线上网功能的 WAP 手机(这时候是 XML->WML,也就是由一种词汇表转换为另一种词汇表,WML 本身就是 XML 的一种词汇表),无非就是另外再写一套 XSLT 样式单。在这种应用中,只包含数据的 XML 相当于 MVC 中的 Model,而表示显示格式的 XSLT 相当于 MVC 中的 View。还有一些需要在服务器定期生成静态页面的网站(例如新浪这类新闻网站)也在服务器采用 XML+XSLT 来定期生成静态页面(这类进程一般是以后台方式运行)。
现在 IE 和 Mozilla 两大浏览器都可以很好地支持 XSLT,所以从技术上讲可以完全采用 XML+XSLT 来制作所有的页面。在这里 XML+XSLT 与我们熟悉的 HTML+CSS 的功能大致相同。这也引起了另外的一种思考,究竟是在服务器端做 XSLT 转换好还是在浏览器端做 XSLT 转换好?我的考虑是尽管在服务器端做 XSLT 转换更加灵活,功能更加强大,我却更愿意在浏览器端做转换。因为 XSLT 转换是非常耗费资源的操作,如果在服务器端做大量的转换操作必然会极大地影响服务器的性能。必须要在服务器端做转换的话一个更好的方案是采用后台方式定期生成静态的 HTML 页面(也就是上面说的一些新闻网站所采取的做法),或者在服务器端通过软件实现某种 cache。这也符合我们一贯的思路:浏览器其实能做很多事情,而且现在主流机型(P4 2G、256M 以上内存)的处理能力已经非常强了,所以在浏览器力所能及的情况下应该把尽量多的工作交给浏览器去做。
就象刚才讲的 XML+CSS,我们来直观地看一下 XML+XSLT 是什么样子:
XML 文档 test.xml:
java代码: |
1 <?xml version=" 1. 0" encoding="gbk"?> 2 <?xml-stylesheet href="test. xsl" type="text/xsl"?> 3 <产品搜寻> 4 <摘要>搜寻字串:“滑鼠 键盘”,共找到 2 笔</摘要> 5 <产品> 6 <货号> 12478943</货号> 7 <品名>手不痛健康滑鼠</品名> 8 <定价>$ 234</定价> 9 <说明页 网址="http: //foo.bar/mouse/12478943">上市发表会</说明页> 10 </产品> 11 <产品> 12 <货号> 83424723</货号> 13 <品名>打不响静悄悄键盘</品名> 14 <定价>$ 567</定价> 15 <说明页 网址="http: //foo.bar/kbd/83424723">产品特性</说明页> 16 </产品> 17 </产品搜寻> |
XSLT 样式单 test.xsl:
java代码: |
1 <?xml version=" 1. 0" encoding="gbk"?> 2 <xsl:stylesheet version=" 1. 0" xmlns:xsl="http: //www.w3.org/1999/XSL/Transform"> 3 <xsl:output encoding="gbk"/> 4 5 <xsl:template match="/"> 6 <html> 7 <head> 8 <title>产品搜寻结果</title> 9 </head> 10 <body> 11 <h1>产品搜寻结果</h1> 12 <p><b>摘要:</b><xsl:value-of select="*/摘要"/></p> 13 <xsl:apply-templates select="产品搜寻"/> 14 </body> 15 </html> 16 </xsl:template> 17 18 <xsl:template match="产品搜寻"> 19 <table> 20 <tr> 21 <th>品名</th> 22 <th>定价</th> 23 <th>说明页</th> 24 </tr> 25 <xsl:for-each select="产品"> 26 <tr> 27 <td><xsl:value-of select="品名"/></td> 28 <td><xsl:value-of select="定价"/></td> 29 <td><a href=" {说明页/@网址 }"><xsl:value-of select="说明页"/></a></td> 30 </tr> 31 </xsl:for-each> 32 </table> 33 </xsl:template> 34 35 </xsl:stylesheet> |
这个例子是一个可以在 Mozilla 中正常显示的 XML 页面(2.jpg),从这个完整的例子中可以对我们刚才学到的知识产生很多感性认识。
a、XML 中的元素、属性名都可以使用中文,只要在 XML 声明中使用了正确的 encoding(这里是 gbk)。
b、在 XSLT 样式单中大量采用 XPath 语法来定位 DOM 中的节点并且对符合条件的节点进行转换。事实上 XPath 的产生最初就是因为 XSLT 的需要。
c、在 XSLT 样式单中同样可以使用名字空间。
d、XML 经 XSLT 转换后输出内容为标准的 HTML 页面,可以在浏览器中正常显示。
e、XSLT 转换是与有效性验证完全无关的操作,只要是满足格式正确的要求的 XML 就可以进行转换。
转换后生成的 HTML 其实是这样的:
java代码: |
1 <html> 2 <head> 3 <title>产品搜寻结果</title> 4 </head> 5 <body> 6 <h1>产品搜寻结果</h1> 7 <p><b>摘要:</b>搜寻字串:“滑鼠 键盘”,共找到 2 笔</p> 8 <table> 9 <tr> 10 <th>品名</th> 11 <th>定价</th> 12 <th>说明页</th> 13 </tr> 14 <tr> 15 <td>手不痛健康滑鼠</td> 16 <td>$ 234</td> 17 <td><a href="http: //foo.bar/kbd/83424723">上市发表会</a></td> 18 </tr> 19 <tr> 20 <td>打不响静悄悄键盘</td> 21 <td>$ 567</td> 22 <td><a href="http: //foo.bar/mouse/12478943">产品特性</a></td> 23 </tr> 24 </table> 25 </body> 26 </html> |
上面这个例子其实是来自于《无废话 XML》这本书。这个例子要在 IE 中正常显示只需要改一点点内容,这个任务留给你们课后去做。
关于 XSLT 首先应该看《无废话 XML》这本书,这本书里对 XSLT 的讲解是很清楚的。XSLT 我们在目前的开发中也用不到,但是 XSLT 在 XML 中是非常基础的知识,所以我在这一部分中用了较大的篇幅来讲述。
知道了这么多,你可能会认为 HTML+CSS 已经是是落后的技术,应该完全被 XML+XSLT 取代。这其实是一个错误的观念,连 W3C 都没有敢肯定 HTML+CSS 一定会被 XML+XSLT 取代。XML+XSLT 的主要缺点是成本和复杂性。由于缺乏所见即所得的(WYSIWYG)页面编辑器的支持,对于实现相同显示效果的复杂页面,XML+XSLT 的工作量要比 HTML+CSS 大得多。而且页面制作人员完全不能适应 XML+XSLT 的工作方式(这是程序员的工作方式),强求他们采用 XML+XSLT 是不现实的。目前 Dreamweaver 已经可以非常好地支持 CSS 了。所以在可预测的未来很长一段时间内,HTML+CSS 仍然是 Web 页面开发的主流技术。当然 HTML 将逐渐被 XHTML 所代替,但是这是另外的问题。XHTML 就是用 XML 来改造 HTML,使 HTML 至少符合 XML 格式正确的要求。符合 XHTML 标准的页面还有一些其它要求,感兴趣的可以看看《XML 高级编程》这本书。
以上这些就是我这次培训的所有内容。这些都是我认为的关于 XML 的基础知识,希望通过这次讲解以及课后的学习能够掌握好。掌握好了这些知识,再去掌握其它与 XML 相关的知识就是轻而易举的了。
相关资料:
1、《无废话 XML》
2、《W3C XML 规范》
3、《XML 高级编程》
4、《Java 与 XML》
5、《XML Schemas》
术语列表:
HTML:HyperText Markup Language,超文本标记语言
SGML:Standard Generalized Markup Language,标准通用标记语言。
XML:eXtensible Markup Language,可扩展标记语言。
DTD:Document Type Definition,文档类型定义。
XML Schema:XML 模式
RELAX NG:REgular LAnguage description for XML Next Generation,下一代的 RELAX。
DSSSL:Document Style Semantics and Specification Language,文档样式语义和规范语言
XSLT:eXtensible Stylesheet Language——Transformation,可扩展样式单语言转换部分
PI:Processing Instruction,处理指令。
XPath:XML 路径描述。
namespace:名字空间。
DOM:Document Object Model,文档对象模型。
SAX:Simple API for XML,XML 简单 API。
JAXP:Java API for XML Parsing
JDOM:Java Document Object Model
DOM4J:Document Object Model for Java
java代码: |
1 <?xml version=" 1. 0" encoding="gbk"?> 2 <DocRoot xmlns:xsi= 'http://www.w3.org/2001/XMLSchema-instance' xsi:schemaLocation="http: //www.xxx.com/dagl ./../mlzx/schemas/dz/wjnr.xsd" xmlns="http://www.xxx.com/dagl"> 3 ... 4 </DocRoot> |
我们可以看到在这个 XML 中声明了两个名字空间。声明名字空间使用 xmlns:前缀=URI 的形式,其中前缀也可以没有,用来设置 XML 文档缺省的名字空间。可以在任何元素上声明名字空间,声明后名字空间在该元素管辖范围(作用域)内起作用。在这个 XML 文档中使用了 Schema 来做有效性验证,因此必须声明 Schema 的名字空间。前缀可以是任意字符串(通常使用 xsi),但是这个名字空间所对应的 URI 是固定的,代表的是一个 Schema 的实例(Schema 与 XML 文档的关系就象是类和对象的关系一样)。名字空间声明后立即可以使用,所以在后面紧接着用这个名字空间前缀 xsi 设置了一个属性 schemaLocation,这个属性用来设置 Schema 文件的路径。xmlns="http://www.xxx.com/dagl" 中没有前缀设置了文档的缺省名字空间,也就是说在本文档中所有没有前缀的元素、属性都属于这个缺省的名字空间。
我们再来看看在 Schema 文件中使用名字空间的情况:
java代码: |
1 <?xml version=" 1. 0" encoding="gbk"?> 2 <schema xmlns="http: //www.w3.org/2001/XMLSchema" 3 xmlns:dagl="http: //www.xxx.com/dagl" 4 targetNamespace="http: //www.xxx.com/dagl" 5 elementFormDefault="qualified"> 6 7 <complexType name="attrType"> 8 <simpleContent> 9 <extension base="string"> 10 <attribute name="code_category" type="string" /> 11 <attribute name="id" type="string" /> 12 </extension> 13 </simpleContent> 14 </complexType> 15 16 <complexType name="dmType"> 17 <sequence> 18 ... 19 <element name="flh" type="dagl:attrType"/> 20 ... 21 </sequence> 22 </complexType> 23 ... 24 </schema> |
这个摘自刚才那个 XML 文档对应的 Schema。我们看到这个 Schema 中除了缺省的名字空间外还声明了一个前缀为 dagl 的名字空间,而且这个名字空间的 URI 必须与 targetNamespace 的 URI 相同。我把 Schema 规范设置为缺省名字空间是为了简化编写 Schema 定义,如果不这样做,那么每一个 complexType、sequence、element 都需要加上前缀(通常使用 xsd),那样是很麻烦的。targetNamespace 用来定义相应的 XML 文档(XML 实例,xsi)应该遵守的名字空间,也就是 XML 实例的缺省名字空间。为什么要这样做我不细讲了,这里的规则就是在 Schema 中自定义的类型(这里是 attrType)使用时必须加上名字空间前缀,这个前缀对应的名字空间 URI 必须与 targetNamespace 的 URI 相同。
现在我们对于 DTD、Schema、名字空间的作用都有了比较清晰的了解。在这一部分需要重点掌握的是名字空间的作用和使用方法。做 DTD、Schema 验证的具体方法可以看我发在 bbs 上的文章:介绍一下做 Schema 验证和 DTD 验证的方法
http://forum.hibernate.org.cn/viewtopic.php?t=859
用 Xerces 来做 DTD 和 Schema 验证是非常简单的。想要更详细地了解 DTD 和 Schema 的开发方法可以看《XML Schemas》和《XML 高级编程》这两本书。
因为性能上的原因,大部分情况下我们做 XML 开发时是不需要验证 XML 的有效性的。不过我们将来有可能会开发自己的 XML 词汇表,所以我们还是有必要学习相关知识的。我们将来主要会采用 Schema,需要重点学习这方面的知识,DTD 仅仅作为需要了解的知识。还有一个采用 Schema 而不采用 DTD 的重要原因是 DTD 不支持名字空间。由于 DTD 来自于 SGML,SGML 中完全没有名字空间的概念,因此 DTD 也完全不能识别 XML 中的名字空间。如果在 XML 中使用了包含了前缀的元素名,例如 <book:title>,用 DTD 做有效性验证时就会出错,DTD 顽固地认为你必须在相应 DTD 文件中定义一个 book:title 元素才行(它不会认为这个 book:title 其实就是一个 title)。而 Schema 完全支持名字空间,正是因为这个原因,而且考虑到名字空间的重要性,虽然目前 Schema 还存在缺陷,我们还是应该坚持使用 Schema 而不使用 DTD。
在目前阶段,只需要掌握好名字空间就可以了。Schema、DTD 等内容可以在需要的时候再去学习。
5、XML 与显示的结合
第 1 部分说到过,XML 的设计目标就是把内容与显示格式分离开。其实只有面向文档的应用才有显示的需求,面向数据的应用不需要定义显示格式。把内容与显示格式分离不仅有利于面向文档应用的开发,更有利于面向数据应用的开发。我来举个面向数据应用的例子,假设你做了一个 Web 搜索引擎(或者叫做网络爬虫),如果页面采用 HTML 来开发,当你的程序读到这样的信息:
java代码: |
1 <tr> 2 <td>XML 高级编程</td> 3 <td> 95. 00</td> 4 </tr> |
你的程序能辨别出这其实是一本书的名称和价格吗?尤其是当这些单纯表示格式的 <tr>、<td> 以不规则方式嵌套了很多层的时候,开发出一个支持 HTML 全文检索的搜索引擎简直是一场灾难。但是如果这些页面都是采用 XML 来开发的,并且内容与显示格式完全分离,当你的程度读到这样的信息:
java代码: |
1 <book> 2 <title>XML 高级编程</title> 3 <price> 95. 00</price> 4 </book> |
就可以非常容易地辨别出这是一本书的名称和价格,尤其是当这个 XML 页面使用了通用的词汇表并且通过了有效性验证时。所以 XML 就是搜索引擎开发者的福音,XML 还会催生出一大堆面向数据的应用,而这类应用在 XML 出现前是根本无法实现的。
言归正传,现在我们来谈谈面向文档的应用。那么显示格式用什么来定义呢?有两种方法,CSS 和 XSLT,XML 文档要在浏览器中显示必须结合 CSS 或者 XSLT 的样式单。XML 文档与 HTML 一样可以用 CSS 来定义显示格式(这里有个例子:http://www.wespoke.com/atom.xml)。
我们先看一下上面这个例子中如何用 CSS 来定义显示格式:
java代码: |
1 <?xml version=" 1. 0" encoding="UTF- 8"?> 2 <?xml-stylesheet href="/css/atom. css" type="text/css"?> 3 <feed> 4 ... 5 <entry> 6 ... 7 </entry> 8 ... 9 </feed> |
其中
java代码: |
1 <?xml-stylesheet href="/css/atom. css" type="text/css"?> |
是与样式单相关的处理指令,指出本文档使用哪一个样式单来定义显示格式,如果用 CSS 来定义,type 就是 "text/css",用 XSLT 来定义,type 就是 "text/xsl"。
下面是其对应的 CSS 文件中的一段:
java代码: |
1 entry {
...}
2 display: block;
3 border: 2px solid black; 4 margin: 1em; 5 padding: 0.5em; 6 } |
可以看出,这段 CSS 与我们在 HTML 中使用的 CSS 没有什么区别。而且通过这段 CSS 可以想象出,浏览器在将在 entry 之外显示 2 个象素宽的边界。浏览器显示的结果与我们想象的完全相同(1.jpg)。
下面我来讲一讲 XSLT,因为在 XML 的世界里 XSLT 是比 CSS 更加重要的技术。现在最流行的 XML 文档的显示方法还是 XSLT。原因与 Schema 一样,XSLT 采用 XML 格式(CSS 采用自己的语法),可以用相同的 XML 解析器解析,便于程序处理。
XSLT 是参考 SGML 中的 DSSSL(定义 SGML 的显示格式)而设计的。最初叫做 XSL,但是后来 W3C 发现工作量实在太大就分成了两个规范:XSLT 和 XSL:FO。XSLT 很快就作为正式规范推出了,主要是面向转换类应用的;XSL:FO 主要是面向精确的格式定义(例如 PDF),正式规范才推出不久。我们主要用到 XSLT,因为 XSLT 目前已经达到了实用的阶段并且被广泛采用。XSLT 其实不完全是为显示目的设计的,XSLT 的主要作用是将 XML 由一种格式转换为另一种格式,例如由 XML 的一种词汇表转换为另一种词汇表,或者由 XML 转换为 HTML 或者 XHTML,便于在浏览器中显示。后一种应用(XML->HTML/XHTML)是目前 XSLT 应用最广的领域,被应用于很多符合 J2EE 标准的表示层框架中。这类框架在服务器端做 XSLT 转换,将生成的 HTML/XHTML 发给浏览器。基于这种技术的框架还可以非常方便地支持各种瘦客户端,例如 PDA 或具有无线上网功能的 WAP 手机(这时候是 XML->WML,也就是由一种词汇表转换为另一种词汇表,WML 本身就是 XML 的一种词汇表),无非就是另外再写一套 XSLT 样式单。在这种应用中,只包含数据的 XML 相当于 MVC 中的 Model,而表示显示格式的 XSLT 相当于 MVC 中的 View。还有一些需要在服务器定期生成静态页面的网站(例如新浪这类新闻网站)也在服务器采用 XML+XSLT 来定期生成静态页面(这类进程一般是以后台方式运行)。
现在 IE 和 Mozilla 两大浏览器都可以很好地支持 XSLT,所以从技术上讲可以完全采用 XML+XSLT 来制作所有的页面。在这里 XML+XSLT 与我们熟悉的 HTML+CSS 的功能大致相同。这也引起了另外的一种思考,究竟是在服务器端做 XSLT 转换好还是在浏览器端做 XSLT 转换好?我的考虑是尽管在服务器端做 XSLT 转换更加灵活,功能更加强大,我却更愿意在浏览器端做转换。因为 XSLT 转换是非常耗费资源的操作,如果在服务器端做大量的转换操作必然会极大地影响服务器的性能。必须要在服务器端做转换的话一个更好的方案是采用后台方式定期生成静态的 HTML 页面(也就是上面说的一些新闻网站所采取的做法),或者在服务器端通过软件实现某种 cache。这也符合我们一贯的思路:浏览器其实能做很多事情,而且现在主流机型(P4 2G、256M 以上内存)的处理能力已经非常强了,所以在浏览器力所能及的情况下应该把尽量多的工作交给浏览器去做。
就象刚才讲的 XML+CSS,我们来直观地看一下 XML+XSLT 是什么样子:
XML 文档 test.xml:
java代码: |
1 <?xml version=" 1. 0" encoding="gbk"?> 2 <?xml-stylesheet href="test. xsl" type="text/xsl"?> 3 <产品搜寻> 4 <摘要>搜寻字串:“滑鼠 键盘”,共找到 2 笔</摘要> 5 <产品> 6 <货号> 12478943</货号> 7 <品名>手不痛健康滑鼠</品名> 8 <定价>$ 234</定价> 9 <说明页 网址="http: //foo.bar/mouse/12478943">上市发表会</说明页> 10 </产品> 11 <产品> 12 <货号> 83424723</货号> 13 <品名>打不响静悄悄键盘</品名> 14 <定价>$ 567</定价> 15 <说明页 网址="http: //foo.bar/kbd/83424723">产品特性</说明页> 16 </产品> 17 </产品搜寻> |
XSLT 样式单 test.xsl:
java代码: |
1 <?xml version=" 1. 0" encoding="gbk"?> 2 <xsl:stylesheet version=" 1. 0" xmlns:xsl="http: //www.w3.org/1999/XSL/Transform"> 3 <xsl:output encoding="gbk"/> 4 5 <xsl:template match="/"> 6 <html> 7 <head> 8 <title>产品搜寻结果</title> 9 </head> 10 <body> 11 <h1>产品搜寻结果</h1> 12 <p><b>摘要:</b><xsl:value-of select="*/摘要"/></p> 13 <xsl:apply-templates select="产品搜寻"/> 14 </body> 15 </html> 16 </xsl:template> 17 18 <xsl:template match="产品搜寻"> 19 <table> 20 <tr> 21 <th>品名</th> 22 <th>定价</th> 23 <th>说明页</th> 24 </tr> 25 <xsl:for-each select="产品"> 26 <tr> 27 <td><xsl:value-of select="品名"/></td> 28 <td><xsl:value-of select="定价"/></td> 29 <td><a href=" {说明页/@网址 }"><xsl:value-of select="说明页"/></a></td> 30 </tr> 31 </xsl:for-each> 32 </table> 33 </xsl:template> 34 35 </xsl:stylesheet> |
这个例子是一个可以在 Mozilla 中正常显示的 XML 页面(2.jpg),从这个完整的例子中可以对我们刚才学到的知识产生很多感性认识。
a、XML 中的元素、属性名都可以使用中文,只要在 XML 声明中使用了正确的 encoding(这里是 gbk)。
b、在 XSLT 样式单中大量采用 XPath 语法来定位 DOM 中的节点并且对符合条件的节点进行转换。事实上 XPath 的产生最初就是因为 XSLT 的需要。
c、在 XSLT 样式单中同样可以使用名字空间。
d、XML 经 XSLT 转换后输出内容为标准的 HTML 页面,可以在浏览器中正常显示。
e、XSLT 转换是与有效性验证完全无关的操作,只要是满足格式正确的要求的 XML 就可以进行转换。
转换后生成的 HTML 其实是这样的:
java代码: |
1 <html> 2 <head> 3 <title>产品搜寻结果</title> 4 </head> 5 <body> 6 <h1>产品搜寻结果</h1> 7 <p><b>摘要:</b>搜寻字串:“滑鼠 键盘”,共找到 2 笔</p> 8 <table> 9 <tr> 10 <th>品名</th> 11 <th>定价</th> 12 <th>说明页</th> 13 </tr> 14 <tr> 15 <td>手不痛健康滑鼠</td> 16 <td>$ 234</td> 17 <td><a href="http: //foo.bar/kbd/83424723">上市发表会</a></td> 18 </tr> 19 <tr> 20 <td>打不响静悄悄键盘</td> 21 <td>$ 567</td> 22 <td><a href="http: //foo.bar/mouse/12478943">产品特性</a></td> 23 </tr> 24 </table> 25 </body> 26 </html> |
上面这个例子其实是来自于《无废话 XML》这本书。这个例子要在 IE 中正常显示只需要改一点点内容,这个任务留给你们课后去做。
关于 XSLT 首先应该看《无废话 XML》这本书,这本书里对 XSLT 的讲解是很清楚的。XSLT 我们在目前的开发中也用不到,但是 XSLT 在 XML 中是非常基础的知识,所以我在这一部分中用了较大的篇幅来讲述。
知道了这么多,你可能会认为 HTML+CSS 已经是是落后的技术,应该完全被 XML+XSLT 取代。这其实是一个错误的观念,连 W3C 都没有敢肯定 HTML+CSS 一定会被 XML+XSLT 取代。XML+XSLT 的主要缺点是成本和复杂性。由于缺乏所见即所得的(WYSIWYG)页面编辑器的支持,对于实现相同显示效果的复杂页面,XML+XSLT 的工作量要比 HTML+CSS 大得多。而且页面制作人员完全不能适应 XML+XSLT 的工作方式(这是程序员的工作方式),强求他们采用 XML+XSLT 是不现实的。目前 Dreamweaver 已经可以非常好地支持 CSS 了。所以在可预测的未来很长一段时间内,HTML+CSS 仍然是 Web 页面开发的主流技术。当然 HTML 将逐渐被 XHTML 所代替,但是这是另外的问题。XHTML 就是用 XML 来改造 HTML,使 HTML 至少符合 XML 格式正确的要求。符合 XHTML 标准的页面还有一些其它要求,感兴趣的可以看看《XML 高级编程》这本书。
以上这些就是我这次培训的所有内容。这些都是我认为的关于 XML 的基础知识,希望通过这次讲解以及课后的学习能够掌握好。掌握好了这些知识,再去掌握其它与 XML 相关的知识就是轻而易举的了。
相关资料:
1、《无废话 XML》
2、《W3C XML 规范》
3、《XML 高级编程》
4、《Java 与 XML》
5、《XML Schemas》
术语列表:
HTML:HyperText Markup Language,超文本标记语言
SGML:Standard Generalized Markup Language,标准通用标记语言。
XML:eXtensible Markup Language,可扩展标记语言。
DTD:Document Type Definition,文档类型定义。
XML Schema:XML 模式
RELAX NG:REgular LAnguage description for XML Next Generation,下一代的 RELAX。
DSSSL:Document Style Semantics and Specification Language,文档样式语义和规范语言
XSLT:eXtensible Stylesheet Language——Transformation,可扩展样式单语言转换部分
PI:Processing Instruction,处理指令。
XPath:XML 路径描述。
namespace:名字空间。
DOM:Document Object Model,文档对象模型。
SAX:Simple API for XML,XML 简单 API。
JAXP:Java API for XML Parsing
JDOM:Java Document Object Model
DOM4J:Document Object Model for Java