DOM心得

DOM 心得

绯雨闲丸

本文介绍了 DOM(文档对象模型)的结构和常规使用方法。通过本文,读者可以学会用DOM来对XML文档进行常见的处理。本文不讨论DOM的设计和实现技巧。

关键词:

XML DOM

概述

DOM (文档对象模型)是对 XML 数据的描述体系,它用树型结构的文档来保存 XML 数据。此外, DOM 也包括了解析、处理 XML 数据的 API 。

在开始使用 DOM 之前,首先来了解一下它的结构。 DOM 整体上的结构是一个 Composite 模式。所有的 XML 单元,无论是文档、元素还是属性、文本,在 DOM 中都是一个 Node (节点)。按照 Composite 模式的定义,每个 Node 都可以包容其他的 Node ,于是很轻松地就构成了一个树型结构。举一个简单的例子,下面的 XML 文档

< Book >

< Title > Effective C++ < Author >

** ** < Name > Scott Meyers ** ** < Gender > Male ** ** < Nationality > USA


** ** < Publisher > Addison-Wesley


在 DOM 中的存储形式就会是这样:

既然已经了解了 DOM 文档的结构,下面就该学习如何操作 DOM 文档了。对于这样一个树型结构,比较重要的操作有文档生成、文档遍历、节点内容的处理(读取、修改等等)、节点本身的操作(插入、删除、替换等等)以及文档的序列化。下面,我们将逐个学习这些操作。

DOM 文档的生成

用 DOM 处理 XML 数据,首先需要以下三个步骤:

1. 创建 DocumentBuilderFactory 。该对象将创建 DocumentBuilder 。

2. 创建 DocumentBuilder 。 DocumentBuilder 将对输入实际进行解析以创建 Document 对象。

3. 解析输入的 XML ,创建 Document 对象。

DocumentBuilderFactory 是一个 Singleton ,所以不能直接 new 出来,应该调用 DocumentBuilderFactory.newInstance() 来得到 DocumentBuilderFactory 的实例。此外, DocumentBuilderFactory 也是一个对象工厂(从名字就能看出),可以用它来创建 DocumentBuilder 。

通常会使用 DocumentBuilder 的 parse 方法返回一个 Document 对象(需要插一句: Document 只是一个接口,用 javax.xml.parsers.DocumentBuilder 的 parse 方法得到的实际上是 org.apache.crimson.tree.XmlDocument 对象)。 parse 方法接受很多输入参数,包括 File 、 InputStream 、 InputSource 、 String 型的 URI 等等。 parse 方法会把输入源进行解析,在内存中生成一个 DOM 的树型结构—— Document 对象。

上面这三个步骤的常用代码如下:

File docFile = new File( "orders.xml" );

Document doc = null ;

try {

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

DocumentBuilder db = dbf.newDocumentBuilder();

doc = db.parse(docFile);

} catch (Exception e) { System.out.print( "Problem parsing the file." ); }

parse 方法可能会抛出 IOException 或者 SAXException ,分别表示输入异常和解析异常。

在创建 DocumentBuilder 之前,可以为 DocumentBuilder 设置一些参数,调节它在生成 Document 时的行为方式。可控的参数包括:

l setCoalescing :确定解析器是否将 CDATA 节点转成文本,并将 CDATA 节点与其周围的文本节点合并(如果合适)。缺省值是 false 。

l setExpandEntityReferences :确定是否扩展外部实体引用。如果为 true ,则将外部数据插入文档。缺省值是 true 。

l setIgnoringcomments :确定是否忽略文件中的注释。缺省值是 false 。

l setIgnoringElementContentWhitespace :确定是否忽略元素内容中的空白(类似于浏览器处理 HTML 的方式)。缺省值是 false 。

l setNameSpaceAware :确定解析器是否注意名称空间信息。缺省值是 false 。

l setValidating :缺省情况下,解析器将不验证文档。将该参数设置为 true 以打开验证。

设置参数的语句如下:

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

dbf.setValidating(true);

DocumentBuilder db = dbf.newDocumentBuilder();

DOM 文档的遍历

DOM 采用了 Composite 模式。 Node 类是所有 XML 单元的基类, Element 、 Attr 、 Document 等等都是 Node 的派生类。每个 Node 都可以包容其他的 Node ,也可以包容文本格式的内容。所以, DOM 文档的遍历相当简单。

首先要获取文档的根节点。用 Document.getDocumentElement() 方法可以得到一个 Element 类型的对象,它就是文档的根节点。对于一个 HTML 文档, getDocumentElement() 方法得到的就是

 1<html> 节点。 
 2
 3只要得到根节点,就可以用  Node.getChildNodes()  方法得到该节点的所有直接子节点,从而遍历整个树型结构。另外,可以用  Node.hasChildNodes()  方法判断一个节点是否叶节点,从而得到遍历算法的结束条件。  getChildNodes()  方法的返回值是  NodeList  对象,  NodeList  有两个方法:  int getLength()  和  Node item(int)  ,可以使用这两个方法来安全地访问其中的每个元素。 
 4
 5上面这种方法是深度优先的遍历(采用迭代算法),还有一种方法是广度优先的遍历算法,要使用的方法是  getFirstChild()  (获取第一个孩子节点)和  getNextSibling()  (获取下一个兄弟节点)。 
 6
 7###  处理元素的内容 
 8
 9首先必须搞清楚“节点(  node  )”和“元素(  element  )”的概念:在  DOM  中节点和元素不是等价的。“元素”是指一对标记(  tag  )及其内部包含的字符串值的总和,例如下面这就是一个元素: 
10
11&lt; Country  &gt;
12
13**China **
14
15
16
17但是它却不是一个节点,而是两个。第一个节点是  <country> 节点,它的值是  null  ;第二个节点是一个文本节点(节点名是  #text  ),它的值是  "China\n"  。文本节点是  <country> 节点的子节点。 
18
19所以,要处理一个元素的内容时,需要两个步骤: 
20
211\.  找到代表该元素的节点; 
22
232\.  处理该节点的第一个子节点; 
24
25只要知道某个元素的名称,就可以用  Element.getElementsByTagName(String name)  方法来找到所有代表该元素的节点。  getElementsByTagName  方法会自动遍历整个树型结构,将找到的节点全部保存在一个  NodeList  中返回。由于  DOM  的树型结构是建立在内存中的,所以这个操作不会太慢。找到节点之后,用  Node.getFirstChild()  方法就可以得到代表该元素值的文本节点,用  Node.setNodeValue(String)  方法就可以修改节点的值。 
26
27###  处理其他类型节点的内容 
28
29如果要访问的节点是属性节点(  Node.getNodeType()==ATTRIBUTE_NODE  ),则可以通过  getAttributes()  方法获得节点中所有的属性。  getAttributes  方法会返回一个  NamedNodeMap  型的对象,这是一个名  \-  值映射表,可以通过  String  型的名称来随机访问,也可以通过  int  型的顺序号来顺序访问。  Attr  类(属性节点)有  getValue()  和  setValue()  两个  accessor  ,用于访问属性的值。 
30
31节点共有  12  种不同的类型,这里只介绍了元素节点和属性节点这两种最常用的,其他的就要自己查帮助了。  Node  有一个  getNodeType()  方法,会返回  short  型值,从而判断一个对象的真实类型,起到  RTTI  的作用。下面是  getNodeType()  方法所有可能的返回值: 
32
33public  static  final  short  ELEMENT_NODE  = 1; 
34
35public  static  final  short  ATTRIBUTE_NODE  = 2; 
36
37public  static  final  short  TEXT_NODE  = 3; 
38
39public  static  final  short  CDATA_SECTION_NODE  = 4; 
40
41public  static  final  short  ENTITY_REFERENCE_NODE  = 5; 
42
43public  static  final  short  ENTITY_NODE  = 6; 
44
45public  static  final  short  PROCESSING_INSTRUCTION_NODE = 7; 
46
47public  static  final  short  COMMENT_NODE  = 8; 
48
49public  static  final  short  DOCUMENT_NODE  = 9; 
50
51public  static  final  short  DOCUMENT_TYPE_NODE  = 10; 
52
53public  static  final  short  DOCUMENT_FRAGMENT_NODE  = 11; 
54
55public  static  final  short  NOTATION_NODE  = 12; 
56
57###  节点的处理 
58
59对于树型数据结构,常见的节点处理就是节点的插入、删除和替换。  DOM  为这些操作提供了非常简单易用的  API  。 
60
61插入节点可以用  Node.appendChild(Node)  ,也可以用  Node.insertBefore(Node newChild, Node refChild)  ;删除节点可以用  Node.removeChild(Node oldChild)  ;替换节点可以用  Node.replaceChild(Node newChild, Node oldChild)  。  DOM  会自动调整树型结构,删除、替换的操作还会返回  oldChild  这个节点,非常方便。 
62
63文档(  Document  )也是一个节点(  Node  ),所以也可以把节点直接插入到文档中。不过要注意:只有该文档创建出的节点才能插入到该文档中,否则会引发  WRONG_DOCUMENT_ERR  异常。创建节点使用  Document.createXxxx  方法。可以用  cloneNode(boolean deep)  方法来克隆一个节点,用  boolean  型的参数决定是否深度拷贝,但是克隆出的节点也不能插入别的文档中。另外,可以用  Document.importNode(Node importedNode, boolean deep)  方法来引入别的文档中的节点。 
64
65需要处理元素的属性时,可以用  Element.setAttributeNode(Attr newAttr)  来插入属性,用  Element.removeAttribute(String name)  来删除不需要的属性。如果属性有同名的,可以用  Element. removeAttributeNode(Attr oldAttr)  来指定删除某一个属性节点。 
66
67###  文档的序列化 
68
69每个  Element  都覆盖了  toString  方法,所以只要指定某一个  Element  作为根,再调用它的  toString  方法,它就会递归得到其下的整个树型结构,并转换成  String  型对象。只要把这个  String  型对象输出到指定的设备上,就可以得到  XML  文档,非常方便。下面这段代码会生成一个新的  HTML  文档(  HTML  可以说是  XML  的子集),并在标准输出设备上输出。 
70
71Document newdoc =  null  ; 
72
73try  { 
74
75&lt;SPAN style="mso-tab-count</country></country></html>
Published At
Categories with Web编程
Tagged with
comments powered by Disqus