1. XML解析的方式:DOM和SAX解析
W3C制定了一套书写XML分析器的标准接口规范--DOM。除此之外,XML_DEV邮件列表中的成员根据应用的需求也自发地定义了一套对XML文档进行操作的接口规范--SAX。这两种接口规范各有侧重,互有长短,应用都比较广泛。
[也就是说DOM是由W3C指定的标准,而SAX是民间的一种XML解析标准。]
我们给出DOM和SAX在应用程序开发过程中所处地位的示意图。参考图1,从图中可以看出,应用程序不是直接对XML文档进行操作的,而是首先由XML分析器对XML文档进行分析,然后,应用程序通过XML分析器所提供的DOM接口或SAX接口对分析结果进行操作,从而间接地实现了对XML文档的访问。
2. DOM解析
DOM是基于树形结构来处理的。XML文档本身也是树形结构的,它与XML文档是非常吻合的。DOM解析的方式,首先读取XML文档,然后在内存里面形成一棵树形的结构,这就结构就反映出来XML的结构,接下我们就可以遍历这棵树,从根结点到叶子结点,每一个结点都可以认为它是一个元素。然后从结点提取出来元素的内容,元素的属性等信息都可以拿到了。DOM:Document Object Model (文档对象模型)。
DOM也有缺点的,因为要将XML文档读取到内存里面了,如果内容比较大,则其消耗就会高。读取到内存,内存里面构造出来的就是对象,这些对象都是要消耗内存的。这么多对象,必然会消耗内存,正因为有这个缺陷,所以就产生了SAX解析,它就没有这个缺点了。
对于XML应用开发来说,DOM就是一个对象化的XML数据接口,一个与语言无关、与平台无关的标准接口规范 [这个接口是由W3C标准制定的,它是没有实现的,是由各个厂商根据接口的描述把对应的实现类提供出来的。另外它跟语言无关,换句话说Java根据这个接口的实现定义方式用Java代码实现这个接口,同理.Net同样根据这种规范用.Net的语言将其实现出来。接口是一样的,但是实现方式是不一样的,但是接口所实现的功能是一样的。]
3.DOM树
<?xml version="1.0" encoding="gb2312"?> <addressbook> <person sex = "male"> <name>张三</name> <email>zhs@xml.net.cn</email> </person> <person sex = "male"> <name>李四</name> <email>ls@xml.net.cn</email> </person> </addressbook>
从上面的XML文档,可以发现这是个标准XML文档,将其倒过来可以发现它是一个树形结构,则它在内存中形成的对象布局也是一个树形结构,所以可以通过得到文档根元素之后访问所要访问的内容了,如下图所示:
4. DOM模型结构
1) 标准的XML文档
<?xml version="1.0" encoding="gb2312"?> <books> <book> <author>至尊宝</author> <title>倘若时光倒流</title> </book> <book> <author>白晶晶</author> <title>月光宝盒实用大全</title> </book> </books>
2) DOM模型结构
注意上图中的的文档(根节点)与根元素节点的不同,根节点表示的整个文档本身,它是我们访问XML的入口,我们都要从根节点开始,来获取到这个XML文档的DOCUMENT对象,获得到对象之后,就可以得到它的根元素,之后就可以逐层递进了。而根元素节点表示这个文档的根元个根元素节点而已。
要严格区分XML文档树中的根结点与根元素结点:根节点(Document)代表的是XML文档本身,是我们解析XML文档的入口,而根元素结点则表示XML文档的根元素,它对应于XML文档的Root。
5. 最常见的节点类型
1)元素:元素是 XML 的基本构件。典型地,元素可以有其它元素、文本节点或两者兼有来作为其子节点。元素节点还是可以有属性的唯一类型的节点。
2)属性:属性节点包含关于元素节点的信息,但实际上,不认为它是元素的子节点
3)文本:确切来讲,文本节点是:文本。它可以包含许多信息或仅仅是空白。
4)文档(根节点):文档节点是整个文档中所有其它节点的父节点。(根节点不等于根元素节点!)如上图的节点books
[说明]:属性不是元素的子结点,属性是元素的一种固有的特性,元素的子结点是它的子元素。而文本就是普通的空白字符之类的。
还有较不常见的节点类型:CDATA、注释、处理指令,我们在使用XML解析的时候主要遇到类型是一个是元素,一个是元素的内容,一个是属性和属性值,这是我们最常用的。
6. DOM的四个基本接口
在DOM接口规范中,有四个基本的接口:Document,Node,NodeList以及NamedNodeMap。在这四个基本接口中,Document接口是对文档进行操作的入口,它是从Node接口继承过来的。Node接口是其他大多数接口的父类,象Document,Element,Attribute,Text,Comment等接口都是从Node接口继承过来的。NodeList接口是一个节点的集合,它包含了某个节点中的所有子节点。NamedNodeMap接口也是一个节点的集合,通过该接口,可以建立节点名和节点之间的一一映射关系,从而利用节点名可以直接访问特定的节点。这四个基本接口基本上就可以完成大部分的操作了。
1) Document接口
查看JDK文档中Document的说明,这里就不再阐述,注意的是它是位于org.w3c.dom包下的,在DOM树中,Document接口同其他接口之间的关系如下图所示:
2) Node接口
Node接口在整个DOM树中具有举足轻重的地位,DOM接口中有很大一部分接口是从Node接口继承过来的,例如,Element、Attr、CDATASection等接口,都是从Node继承过来的。在DOM树中,Node接口代表了树中的一个节点。一个典型的Node接口如下图所示:同样,查看JDK文档中Node接口的说明,这里不做阐述。
[说明]:w3c 把元素,元素里面的内容,文本,注释、属性都是用Node来表示,所以在解析的时候要经常进行向下类型转换。查看JDK文档,可以发现Element、Attr等的父接口都是Node。
3) NodeList
可以装多个列表,查看JDK文档中的用法,需要注意的是:NodeList中的每个item都可以通过一个索引来访问,该索引值从0开始。
4) NamedNodeMap
NamedNodeMap表示的是一组节点和其唯一名字的一一对应关系,这个接口主要用在属性节点的表示上。NamedNodeMap接口经常都是跟属性打交道的,属性的名字不能重复,它的值可以不要求,可以看出他的特性与Map很相似。
查看JDK文档的介绍和方法,以便更好的使用。
7.练习使用DOM解析XML
下面有一份XML文档,我们现在利用DOM解析将下面文档的4个PERSON的5个信息:NAME、ADDRESS、TEL、FAX、EMAIL提取打印出来。
接下来就是解析的过程,解析过程写在程序的注释中,请参考以下的代码:<?xml version="1.0"?> <PEOPLE> <PERSON PERSONID="E01"> <NAME>Tony Blair</NAME> <ADDRESS>10 Downing Street, London, UK</ADDRESS> <TEL>(061) 98765</TEL> <FAX>(061) 98765</FAX> <EMAIL>blair@everywhere.com</EMAIL> </PERSON> <PERSON PERSONID="E02"> <NAME>Bill Clinton</NAME> <ADDRESS>White House, USA</ADDRESS> <TEL>(001) 6400 98765</TEL> <FAX>(001) 6400 98765</FAX> <EMAIL>bill@everywhere.com</EMAIL> </PERSON> <PERSON PERSONID="E03"> <NAME>Tom Cruise</NAME> <ADDRESS>57 Jumbo Street, New York, USA</ADDRESS> <TEL>(001) 4500 67859</TEL> <FAX>(001) 4500 67859</FAX> <EMAIL>cruise@everywhere.com</EMAIL> </PERSON> <PERSON PERSONID="E04"> <NAME>Linda Goodman</NAME> <ADDRESS>78 Crax Lane, London, UK</ADDRESS> <TEL>(061) 54 56789</TEL> <FAX>(061) 54 56789</FAX> <EMAIL>linda@everywhere.com</EMAIL> </PERSON> </PEOPLE>
/* * DOM解析XML文档的流程,基本都是按照这个流程来走的。 */ package com.ahuier.xml.dom; import java.io.File; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; public class DomTest1 { public static void main(String[] args) throws Exception { /* * Step 1: 获得dom解析器工厂(工厂的作用就是用于创建具体的解析器。) * 获得一个文档构建的工厂,DOM解析器工厂 * 查看JDK文档DoucmentBuilderFactory 它是protected类,所以是不能new的,只能使用它的方法 newInstance() 这种方式很像之前学的单例模式。 */ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); //Step 2: 获得具体的DOM解析器 DocumentBuilder db = dbf.newDocumentBuilder(); /* * 查看DocumentBuilderJDK文档的多个parse()方法, * 选择public Document parse(File f)方法 ,当然你还可以用其他的方式,它返回的是代表整个文档的Document对象,通过这个入口我们就可以往下了 * Step 3:解析一个XML文档,获得Document对象(根节点) */ Document document = db.parse(new File("candidate.xml")); /* * 现在我们想要获得所有的person节点,查看JDK文档中的Document的方法 * 查看NodeList getElementsByTagName(String tagname) */ NodeList list = document.getElementsByTagName("PERSON"); // System.out.println(list.getLength()); //打印出NodeList长度,即节点的个数,PERSON有四个 for(int i = 0; i < list.getLength(); i++){ //item返回的元素是Node,现在我们要的是Element 所以要进行向下类型转换,这一点要心里很清楚。 Element element = (Element)list.item(i); /* * 现在流程进入到了PERSON元素下面了,我们要获得PERSON低下的子元素NAME、ADDRESS等。 * getNodeValue()表示获得当前节点的值 */ /* 注意如果是下面这两行程序打印出来的是4个null空的值 * 原因是因为XML在这个地方:<NAME>Tony Blair</NAME> * 中间的Tony Blair的文本也是看做是一个元素了,下面我们打印的就是这个文本,它永远都是空的,所以我们必须打印出这个文本所对应的的子元素 就是Tony Blair * 所以我们必须用这种方式来getFirstChild()来得到它的内容。 String content = element.getElementsByTagName("NAME").item(0).getNodeValue(); System.out.println(content);*/ String content = element.getElementsByTagName("NAME").item(0).getFirstChild().getNodeValue(); System.out.println("name: " + content); content = element.getElementsByTagName("ADDRESS").item(0).getFirstChild().getNodeValue(); System.out.println("address: " + content); content = element.getElementsByTagName("TEL").item(0).getFirstChild().getNodeValue(); System.out.println("tel: " + content); content = element.getElementsByTagName("FAX").item(0).getFirstChild().getNodeValue(); System.out.println("fax: " + content); content = element.getElementsByTagName("EMAIL").item(0).getFirstChild().getNodeValue(); System.out.println("email: " + content); System.out.println("-------------------------------------"); } } }
[编译执行结果]:
name: Tony Blair
address: 10 Downing Street, London, UK
tel: (061) 98765
fax: (061) 98765
email: blair@everywhere.com
-------------------------------------
name: Bill Clinton
address: White House, USA
tel: (001) 6400 98765
fax: (001) 6400 98765
email: bill@everywhere.com
-------------------------------------
name: Tom Cruise
address: 57 Jumbo Street, New York, USA
tel: (001) 4500 67859
fax: (001) 4500 67859
email: cruise@everywhere.com
-------------------------------------
name: Linda Goodman
address: 78 Crax Lane, London, UK
tel: (061) 54 56789
fax: (061) 54 56789
email: linda@everywhere.com
-------------------------------------
[说明]:上面程序中注意XML文档放的位置,以及相对路径的问题。
DOM解析是非常重要的,工作中经常有人会问解析XML的方式有几种,它们的特点是什么?其实就是一个是DOM方式,一个SAX方式,把他们的解析的过程分析一下就行了。
另外我们之后要做一个多人聊天室的项目,最好能够利用XML的方式来传输这种方式最好的,是最强壮的。而不应该用之前程序写的那种拼接字符串的形式。