DOM心得

类别:.NET开发 点击:0 评论:0 推荐:

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++</Title>

<Author>

  <Name>Scott Meyers</Name>

  <Gender>Male</Gender>

  <Nationality>USA</Nationality>

  </Author>

  <Publisher>Addison-Wesley</Publisher>

  </Book>

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

 

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

DOM文档的生成

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

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

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

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

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

通常会使用DocumentBuilderparse方法返回一个Document对象(需要插一句:Document只是一个接口,用javax.xml.parsers.DocumentBuilderparse方法得到的实际上是org.apache.crimson.tree.XmlDocument对象)。parse方法接受很多输入参数,包括FileInputStreamInputSourceString型的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单元的基类,ElementAttrDocument等等都是Node的派生类。每个Node都可以包容其他的Node,也可以包容文本格式的内容。所以,DOM文档的遍历相当简单。

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

只要得到根节点,就可以用Node.getChildNodes()方法得到该节点的所有直接子节点,从而遍历整个树型结构。另外,可以用Node.hasChildNodes()方法判断一个节点是否叶节点,从而得到遍历算法的结束条件。getChildNodes()方法的返回值是NodeList对象,NodeList有两个方法:int getLength()Node item(int),可以使用这两个方法来安全地访问其中的每个元素。

上面这种方法是深度优先的遍历(采用迭代算法),还有一种方法是广度优先的遍历算法,要使用的方法是getFirstChild()(获取第一个孩子节点)和getNextSibling()(获取下一个兄弟节点)。

处理元素的内容

首先必须搞清楚“节点(node)”和“元素(element)”的概念:在DOM中节点和元素不是等价的。“元素”是指一对标记(tag)及其内部包含的字符串值的总和,例如下面这就是一个元素:

<Country>

China

</Country>

但是它却不是一个节点,而是两个。第一个节点是<Country>节点,它的值是null;第二个节点是一个文本节点(节点名是#text),它的值是"China\n"。文本节点是<Country>节点的子节点。

所以,要处理一个元素的内容时,需要两个步骤:

1.       找到代表该元素的节点;

2.       处理该节点的第一个子节点;

只要知道某个元素的名称,就可以用Element.getElementsByTagName(String name)方法来找到所有代表该元素的节点。getElementsByTagName方法会自动遍历整个树型结构,将找到的节点全部保存在一个NodeList中返回。由于DOM的树型结构是建立在内存中的,所以这个操作不会太慢。找到节点之后,用Node.getFirstChild()方法就可以得到代表该元素值的文本节点,用Node.setNodeValue(String)方法就可以修改节点的值。

处理其他类型节点的内容

如果要访问的节点是属性节点(Node.getNodeType()==ATTRIBUTE_NODE),则可以通过getAttributes()方法获得节点中所有的属性。getAttributes方法会返回一个NamedNodeMap型的对象,这是一个名-值映射表,可以通过String型的名称来随机访问,也可以通过int型的顺序号来顺序访问。Attr类(属性节点)有getValue()setValue()两个accessor,用于访问属性的值。

节点共有12种不同的类型,这里只介绍了元素节点和属性节点这两种最常用的,其他的就要自己查帮助了。Node有一个getNodeType()方法,会返回short型值,从而判断一个对象的真实类型,起到RTTI的作用。下面是getNodeType()方法所有可能的返回值:

    public static final short ELEMENT_NODE                                 = 1;

    public static final short ATTRIBUTE_NODE              = 2;

    public static final short TEXT_NODE                              = 3;

    public static final short CDATA_SECTION_NODE                = 4;

    public static final short ENTITY_REFERENCE_NODE           = 5;

    public static final short ENTITY_NODE                           = 6;

    public static final short PROCESSING_INSTRUCTION_NODE = 7;

    public static final short COMMENT_NODE                      = 8;

    public static final short DOCUMENT_NODE                     = 9;

    public static final short DOCUMENT_TYPE_NODE             = 10;

    public static final short DOCUMENT_FRAGMENT_NODE      = 11;

    public static final short NOTATION_NODE               = 12;

节点的处理

对于树型数据结构,常见的节点处理就是节点的插入、删除和替换。DOM为这些操作提供了非常简单易用的API

插入节点可以用Node.appendChild(Node),也可以用Node.insertBefore(Node newChild, Node refChild);删除节点可以用Node.removeChild(Node oldChild);替换节点可以用Node.replaceChild(Node newChild, Node oldChild)DOM会自动调整树型结构,删除、替换的操作还会返回oldChild这个节点,非常方便。

文档(Document)也是一个节点(Node),所以也可以把节点直接插入到文档中。不过要注意:只有该文档创建出的节点才能插入到该文档中,否则会引发WRONG_DOCUMENT_ERR异常。创建节点使用Document.createXxxx方法。可以用cloneNode(boolean deep)方法来克隆一个节点,用boolean型的参数决定是否深度拷贝,但是克隆出的节点也不能插入别的文档中。另外,可以用Document.importNode(Node importedNode, boolean deep)方法来引入别的文档中的节点。

需要处理元素的属性时,可以用Element.setAttributeNode(Attr newAttr)来插入属性,用Element.removeAttribute(String name)来删除不需要的属性。如果属性有同名的,可以用Element. removeAttributeNode(Attr oldAttr)来指定删除某一个属性节点。

文档的序列化

每个Element都覆盖了toString方法,所以只要指定某一个Element作为根,再调用它的toString方法,它就会递归得到其下的整个树型结构,并转换成String型对象。只要把这个String型对象输出到指定的设备上,就可以得到XML文档,非常方便。下面这段代码会生成一个新的HTML文档(HTML可以说是XML的子集),并在标准输出设备上输出。

Document newdoc = null;

try {

   DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

    DocumentBuilder db = dbf.newDocumentBuilder();

    newdoc = db.newDocument();      

} catch(Exception e){};

 

Element head = newdoc.createElement("Head");

Element title = newdoc.createElement("Title");

title.appendChild(newdoc.createTextNode("Document created by DOM"));

head.appendChild(title);

 

Element body = newdoc.createElement("Body");

body.appendChild(newdoc.createTextNode("This is a test document"));

 

Element newroot = newdoc.createElement("Html");

newroot.appendChild(body);

newroot.insertBefore(head, body);

 

newdoc.appendChild(newroot);

System.out.println(newroot);

总结

DOM在内存中生成树型结构来处理XML数据,在处理速度和方便性上有优势,但是比较耗存储空间。如果XML文档比较大的话,解析过程可能会需要比较长的时间。

本文地址:http://com.8s8s.com/it/it46047.htm