(五)使用DOM解析XML文档

DOM:Document Object Model (文档对象模型)

1、DOM与SAX

W3C制定了一套书写XML分析器的标准接口规范--DOM。除此之外,XML_DEV邮件列表中的成员根据应用的需求也自发地定义了一套对XML文档进行操作的接口规范--SAX。这两种接口规范各有侧重,互有长短,应用都比较广泛。;XML应用程序不是直接对XML文档进行操作的,而是首先由XML分析器对XML文档进行分析,然后,应用程序通过XML分析器所提供的DOM接口或SAX接口对分析结果进行操作,从而间接地实现了对XML文档的访问。

2、DOM的全称是Document Object Model,也即文档对象模型。在应用程序中,基于DOM的XML分析器将一个XML文档转换成一个对象模型的集合(通常称DOM树),应用程序正是通过对这个对象模型的操作,来实现对XML文档数据的操作。通过DOM接口,应用程序可以在任何时候访问XML文档中的任何一部分数据,因此,这种利用DOM接口的机制也被称作随机访问机制。
DOM接口提供了一种通过分层对象模型来访问XML文档信息的方式,这些分层对象模型依据XML的文档结构形成了一棵节点树。无论XML文档中所描述的是什么类型的信息,即便是制表数据、项目列表或一个文档,利用DOM所生成的模型都是节点树的形式。也就是说,DOM强制使用树模型来访问XML文档中的信息。由于XML本质上就是一种分层结构,所以这种描述方法是相当有效的。
DOM树所提供的随机访问方式给应用程序的开发带来了很大的灵活性,它可以任意地控制整个XML文档中的内容。然而,由于DOM分析器把整个XML文档转化成DOM树放在了内存中,因此,当文档比较大或者结构比较复杂时,对内存的需求就比较高。而且,对于结构复杂的树的遍历也是一项耗时的操作。所以,DOM分析器对机器性能的要求比较高,实现效率不十分理想。不过,由于DOM分析器所采用的树结构的思想与XML文档的结构相吻合,同时鉴于随机访问所带来的方便,因此,DOM分析器还是有很广泛的使用价值的。

对于XML应用开发来说,DOM就是一个对象化的XML数据接口,一个与语言无关、与平台无关的标准接口规范。它定义了HTML文档和XML文档的逻辑结构,给出了一种访问和处理HTML文档和XML文档的方法。利用DOM,程序开发人员可以动态地创建文档,遍历文档结构,添加、修改、删除文档内容,改变文档的显示方式等等。可以这样说,文档代表的是数据,而DOM则代表了如何去处理这些数据。无论是在浏览器里还是在浏览器外,无论是在服务器上还是在客户端,只要有用到XML的地方,就会碰到对DOM的应用。
作为W3C的标准接口规范,目前,DOM由三部分组成,包括:核心(core)、HTML和XML。核心部分是结构化文档比较底层对象的集合,这一部分所定义的对象已经完全可以表达出任何HTML和XML文档中的数据了。HTML接口和XML接口两部分则是专为操作具体的HTML文档和XML文档所提供的高级接口,使对这两类文件的操作更加方便。

3、DOM树

[html]  view plain  copy
  1. <?xml version="1.0" encoding="gb2312"?>  
  2. <addressbook>  
  3. <person sex = "male">  
  4. <name>张三</name>  
  5. <email>zhs@xml.net.cn</email>  
  6. </person>  
  7. <person sex = "male">  
  8. <name>李四</name>  
  9. <email>ls@xml.net.cn</email>  
  10. </person>  
  11. </addressbook>  


[html]  view plain  copy
  1. <?xml version="1.0" encoding="gb2312" ?> <books> <book> <author>至尊宝</author> <title>倘若时光倒流</title > </book> <book> <author>白晶晶</author> <title>月光宝盒实用大全</title> </book> </books>  


 

要严格区分XML文档树中的根结点与根元素结点:根节点(Document)代表的是XML文档本身,是我们解析XML文档的入口,而根元素结点则表示XML文档的根元素,它对应于XML文档的Root。

•最常见的节点类型:
–元素:元素是 XML 的基本构件。典型地,元素可以有其它元素、文本节点或两者兼有来作为其子节点。元素节点还是可以有属性的唯一类型的节点。
–属性:属性节点包含关于元素节点的信息,但实际上,不认为它是元素的子节点
–文本:确切来讲,文本节点是:文本。它可以包含许多信息或仅仅是空白。
–文档(根节点):文档节点是整个文档中所有其它节点的父节点。(根节点不等于根元素节点!)
•较不常见的节点类型:CDATA、注释、处理指令

4、DOM的四个基本接口

文档对象模型利用对象来把文档模型化,这些模型不仅描述了文档的结构,还定义了模型中对象的行为。换句话说,在上面给出的例子里,图中的节点不是数据结构,而是对象,对象中包含方法和属性。在DOM中,对象模型要实现:
•用来表示、操作文档的接口
•接口的行为和属性
•接口之间的关系以及互操作
在DOM接口规范中,有四个基本的接口:Document,Node,NodeList以及NamedNodeMap。在这四个基本接口中,Document接口是对文档进行操作的入口,它是从Node接口继承过来的。Node接口是其他大多数接口的父类,象Document,Element,Attribute,Text,Comment等接口都是从Node接口继承过来的。NodeList接口是一个节点的集合,它包含了某个节点中的所有子节点。NamedNodeMap接口也是一个节点的集合,通过该接口,可以建立节点名和节点之间的一一映射关系,从而利用节点名可以直接访问特定的节点。

1)Document接口

Document接口代表了整个XML/HTML文档,因此,它是整棵文档树的根,提供了对文档中的数据进行访问和操作的入口。
由于元素、文本节点、注释、处理指令等都不能脱离文档的上下文关系而独立存在,所以在Document接口提供了创建其他节点对象的方法,通过该方法创建的节点对象都有一个ownerDocument属性,用来表明当前节点是由谁所创建的以及节点同Document之间的联系。
在DOM树中,Document接口同其他接口之间的关系如下图所示:

2)Node接口

Node接口在整个DOM树中具有举足轻重的地位,DOM接口中有很大一部分接口是从Node接口继承过来的,例如,Element、Attr、CDATASection等接口,都是从Node继承过来的。在DOM树中,Node接口代表了树中的一个节点。一个典型的Node接口如下图所示:

3)NodeList接口

NodeList接口提供了对节点集合的抽象定义,它并不包含如何实现这个节点集的定义。NodeList用于表示有顺序关系的一组节点,比如某个节点的子节点序列。另外,它还出现在一些方法的返回值中,例如getElementsByTagName。
在DOM中,NodeList的对象是"live"的,换句话说,对文档的改变,会直接反映到相关的NodeList对象中。例如,如果通过DOM获得一个NodeList对象,该对象中包含了某个Element节点的所有子节点的集合,那么,当再通过DOM对Element节点进行操作(添加、删除、改动节点中的子节点)时,这些改变将会自动地反映到NodeList对象中,而不需DOM应用程序再做其他额外的操作。
NodeList中的每个item都可以通过一个索引来访问,该索引值从0开始。

4)NamedNodeMap接口

实现了NamedNodeMap接口的对象中包含了可以通过名字来访问的一组节点的集合。不过注意,NamedNodeMap并不是从NodeList继承过来的,它所包含的节点集中的节点是无序的。尽管这些节点也可以通过索引来进行访问,但这只是提供了枚举NamedNodeMap中所包含节点的一种简单方法,并不表明在DOM规范中为NamedNodeMap中的节点规定了一种排列顺序。
NamedNodeMap表示的是一组节点和其唯一名字的一一对应关系,这个接口主要用在属性节点的表示上。
与NodeList相同,在DOM中,NamedNodeMap对象也是"live"的。

5、DOM的基本对象

•一切都是节点(对象)
•Node对象:DOM结构中最为基本的对象
•Document对象:代表整个XML的文档
•NodeList对象:包含一个或者多个Node的列表
•Element对象:代表XML文档中的标签元素

DOM处理XML文档步骤:

[html]  view plain  copy
  1. <?xml version="1.0"?>  
  2. <PEOPLE>  
  3.     <PERSON PERSONID="E01">  
  4.         <NAME>Tony Blair</NAME>  
  5.         <ADDRESS>10 Downing Street, London, UK</ADDRESS>  
  6.         <TEL>(061) 98765</TEL>  
  7.         <FAX>(061) 98765</FAX>  
  8.         <EMAIL>blair@everywhere.com</EMAIL>  
  9.     </PERSON>  
  10.     <PERSON PERSONID="E02">  
  11.         <NAME>Bill Clinton</NAME>  
  12.         <ADDRESS>White House, USA</ADDRESS>  
  13.         <TEL>(001) 6400 98765</TEL>  
  14.         <FAX>(001) 6400 98765</FAX>  
  15.         <EMAIL>bill@everywhere.com</EMAIL>  
  16.     </PERSON>  
  17.     <PERSON PERSONID="E03">  
  18.         <NAME>Tom Cruise</NAME>  
  19.         <ADDRESS>57 Jumbo Street, New York, USA</ADDRESS>  
  20.         <TEL>(001) 4500 67859</TEL>  
  21.         <FAX>(001) 4500 67859</FAX>  
  22.         <EMAIL>cruise@everywhere.com</EMAIL>  
  23.     </PERSON>  
  24.     <PERSON PERSONID="E04">  
  25.         <NAME>Linda Goodman</NAME>  
  26.         <ADDRESS>78 Crax Lane, London, UK</ADDRESS>  
  27.         <TEL>(061) 54 56789</TEL>  
  28.         <FAX>(061) 54 56789</FAX>  
  29.         <EMAIL>linda@everywhere.com</EMAIL>  
  30.     </PERSON>  
  31. </PEOPLE>  


 

[java]  view plain  copy
  1. import java.io.File;  
  2.   
  3. import javax.xml.parsers.DocumentBuilder;  
  4. import javax.xml.parsers.DocumentBuilderFactory;  
  5.   
  6. import org.w3c.dom.Document;  
  7. import org.w3c.dom.Element;  
  8. import org.w3c.dom.NodeList;  
  9.   
  10. public class DomTest1  
  11. {  
  12.     public static void main(String[] args) throws Exception  
  13.     {  
  14.         //step1:获得dom解析器工厂(工厂的作用适用于创建具体的解析器)  
  15.         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();  
  16.           
  17.         //setp2:获得具体的dom解析器  
  18.         DocumentBuilder db = dbf.newDocumentBuilder();  
  19.           
  20.         //step3:解析一个xml文档,获得Document对象(根节点)  
  21.         Document document = db.parse(new File("candidate.xml"));  
  22.           
  23.         NodeList list = document.getElementsByTagName("PERSON");  
  24.         for(int i = 0;i<list.getLength();i++)  
  25.         {  
  26.             Element element = (Element)list.item(i);  
  27.             String content = element.getElementsByTagName("NAME").item(0).getFirstChild().getNodeValue();  
  28.             System.out.println("name:" + content);  
  29.             content = element.getElementsByTagName("ADDRESS").item(0).getFirstChild().getNodeValue();  
  30.             System.out.println("address:" + content);  
  31.             content = element.getElementsByTagName("TEL").item(0).getFirstChild().getNodeValue();  
  32.             System.out.println("tel:" + content);  
  33.             content = element.getElementsByTagName("FAX").item(0).getFirstChild().getNodeValue();  
  34.             System.out.println("email:" + content);  
  35.             content = element.getElementsByTagName("EMAIL").item(0).getFirstChild().getNodeValue();  
  36.             System.out.println("--------------------");  
  37.         }  
  38.     }  
  39. }  


6、DOM详解:

我们需要把一个XML文档的内容解析到一个个的Java对象中去供程序使用,利用JAXP,我们只需几行代码就能做到这一点。首先,我们需要建立一个解析器工厂,以利用这个工厂来获得一个具体的解析器对象。JAXP(JAVA API for XML Parsing):用于XML解析的JAVA API。

[html]  view plain  copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <messages> <message>  
  3. Good-bye serialization, hello Java!  
  4. </message>  
  5. </messages>  


•DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
•我们在这里使用DocumentBuilderFacotry的目的是为了创建与具体解析器无关的程序,当DocumentBuilderFactory 类的静态方法newInstance()被调用时,它根据一个系统变量来决定具体使用哪一个解析器。又因为所有的解析器都服从于JAXP所定义的接口,所 以无论具体使用哪一个解析器,代码都是一样的。所以当在不同的解析器之间进行切换时,只需要更改系统变量的值,而不用更改任何代码。这就是工厂所带来的好处。

•DocumentBuilder db = dbf.newDocumentBuilder();
•当获得一个工厂对象后,使用它的静态方法newDocumentBuilder()方法可以获得一个DocumentBuilder对象,这个对象代表了具体的DOM解析器。但具体是哪一种解析器,微软的或者IBM的,对于程序而言并不重要
•然后,我们就可以利用这个解析器来对XML文档进行解析了
•Document doc = db.parse("c:/xml/message.xml");
•DocumentBuilder的parse()方法接受一个XML文档名作为输入参数,返回一个Document对象,这个Document对象就代表了一个XML文档的树模型。以后所有的对XML文档的操作,都与解析器无关,直接在这个Document对象上进行操作就可以了。而具体对Document操作的方法,就是由DOM所定义的了
•从上面得到的Document对象开始,我们就可以开始我们的DOM解析了。使用Document对象的getElementsByTagName()方法,我们可以得到一个NodeList对象,一个Node对象代表了一个XML文档中的一个标签元素,而NodeList对象,所代表的是一个Node对象的列表
•NodeList nl = doc.getElementsByTagName("message");
•我们通过这样一条语句所得到的是XML文档中所有<message>标签对应的Node对象的一个列表。然后,我们可以使用NodeList对象的item()方法来得到列表中的每一个Node对象
•Node my_node = nl.item(0);
•当一个Node对象被建立之后,保存在XML文档中的数据就被提取出来并封装在这个Node中了。在这个例子中,要提取Message标签内的内容,我们通常会使用Node对象的getNodeValue()方法
•String message = my_node.getFirstChild().getNodeValue();
•请注意,这里还使用了一个getFirstChild()方法来获得message下面的第一个子Node对象。虽然在message标签下面除了文本外并没有其它子标签或者属性,但是我们坚持在这里使用getFirstChild()方法,这主要和W3C对DOM的定义有关。W3C把标签内的文本部分也定义成一个Node,所以先要得到代表文本的那个Node,我们才能够使用getNodeValue()来获取文本的内容

 7、•DOM的基本对象有5个:Document,Node,NodeList,Element和Attr
•Document对象代表了整个XML的文档,所有其它的Node,都以一定的顺序包含在Document对象之内,排列成一个树形的结构,程序员可以通过遍历这颗树来得到XML文档的所有的内容,这也是对XML文档操作的起点。我们总是先通过解析XML源文件而得到一个Document对象,然后再来执行后续的操作。此外,Document还包含了创建其它节点的方法,比如createAttribute()用来创建一个Attr对象。它所包含的主要的方法有
•createAttribute(String):用给定的属性名创建一个Attr对象,并可在其后使用setAttributeNode方法来放置在某一个Element对象上面。
•createElement(String):用给定的标签名创建一个Element对象,代表XML文档中的一个标签,然后就可以在这个Element对象上添加属性或进行其它的操作。
•createTextNode(String):用给定的字符串创建一个Text对象,Text对象代表了标签或者属性中所包含的纯文本字符串。如果在一个标签内没有其它的标签,那么标签内的文本所代表的Text对象是这个Element对象的唯一子对象。
•getElementsByTagName(String):返回一个NodeList对象,它包含了所有给定标签名字的标签。
•getDocumentElement():返回一个代表这个DOM树的根元素节点的Element对象,也就是代表XML文档根元素的那个对象。
•Node对象是DOM结构中最为基本的对象,代表了文档树中的一个抽象的节点。在实际使用的时候,很少会真正的用到Node这个对象,而是用到诸如Element、Attr、Text等Node对象的子对象来操作文档。Node对象为这些对象提供了一个抽象的、公共的根。虽然在Node对象中定义了对其子节点进行存取的方法,但是有一些Node子对象,比如Text对象,它并不存在子节点,这一点是要注意的。
•Node对象所包含的主要的方法有
•appendChild(org.w3c.dom.Node):为这个节点添加一个子节点,并放在所有子节点的最后,如果这个子节点已经存在,则先把它删掉再添加进去。
•getFirstChild():如果节点存在子节点,则返回第一个子节点,对等的,还有getLastChild()方法返回最后一个子节点。
•getNextSibling():返回在DOM树中这个节点的下一个兄弟节点,对等的,还有getPreviousSibling()方法返回其前一个兄弟节点。
•getNodeName():根据节点的类型返回节点的名称。
•getNodeType():返回节点的类型。
•getNodeValue():返回节点的值。
•hasChildNodes():判断是不是存在有子节点。
•hasAttributes():判断这个节点是否存在有属性。
•getOwnerDocument():返回节点所处的Document对象。
•insertBefore(org.w3c.dom.Node new,org.w3c.dom.Node ref):在给定的一个子对象前再插入一个子对象。
•removeChild(org.w3c.dom.Node):删除给定的子节点对象
•replaceChild(org.w3c.dom.Node new,org.w3c.dom.Node old):用一个新的Node对象代替给定的子节点对象。
•NodeList对象,顾名思义,就是代表了一个包含了一个或者多个Node的列表。可以简单的把它看成一个Node的数组,我们可以通过方法来获得列表中的元素:
•getLength():返回列表的长度。
•item(int):返回指定位置的Node对象
•Element对象代表的是XML文档中的标签元素,继承于Node,亦是Node的最主要的子对象。在标签中可以包含有属性,因而Element对象中有存取其属性的方法,而任何Node中定义的方法,也可以用在Element对象上面。
•getElementsByTagName(String):返回一个NodeList对象,它包含了在这个标签中其下的子孙节点中具有给定标签名字的标签。
•getTagName():返回一个代表这个标签名字的字符串。
•getAttribute(String):返回标签中给定属性名称的属性的值。在这儿需要注意的是,因为XML文档中允许有实体属性出现,而这个方法对这些实体属性并不适用。这时候需要用到getAttributeNode()方法来得到一个Attr对象来进行进一步的操作
•getAttributeNode(String):返回一个代表给定属性名称的Attr对象。
-Attr对象代表了某个标签中的属性。Attr继承于Node,但是因为Attr实际上是包含在Element中的,它并不能被看作是Element的子对象,因而在DOM中Attr并不是DOM树的一部分,所以Node中的getparentNode(),getpreviousSibling()和getnextSibling()返回的都将是null。也就是说,Attr其实是被看作包含它的Element对象的一部分,它并不作为DOM树中单独的一个节点出现。这一点在使用的时候要同其它的Node子对象相区别

8、解析任意给定的xml文档并在命令行输出:

[java]  view plain  copy
  1. import java.io.File;  
  2.   
  3. import javax.xml.parsers.DocumentBuilder;  
  4. import javax.xml.parsers.DocumentBuilderFactory;  
  5.   
  6. import org.w3c.dom.Attr;  
  7. import org.w3c.dom.Comment;  
  8. import org.w3c.dom.Document;  
  9. import org.w3c.dom.Element;  
  10. import org.w3c.dom.NamedNodeMap;  
  11. import org.w3c.dom.Node;  
  12. import org.w3c.dom.NodeList;  
  13.   
  14. /** 
  15.  * 使用递归解析给定的任意一个XML文档并且将其内容输出到命令行上 
  16.  * @author Admin 
  17.  * 
  18.  */  
  19. public class DomTest3  
  20. {  
  21.     public static void main(String[] args) throws Exception  
  22.     {  
  23.         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();  
  24.         DocumentBuilder db = dbf.newDocumentBuilder();  
  25.         Document doc = db.parse(new File("student.xml"));  
  26.         //获得根元素节点  
  27.         Element root = doc.getDocumentElement();  
  28.         parseElement(root);  
  29.     }  
  30.       
  31.     private static void parseElement(Element element)  
  32.     {  
  33.         String tagName = element.getNodeName();  
  34.           
  35.         NodeList children = element.getChildNodes();  
  36.         System.out.print("<" + tagName);  
  37.           
  38.         //element元素的所有属性所构成的NamedNodeMap对象,需要对其进行判断  
  39.         NamedNodeMap map = element.getAttributes();  
  40.         //如果该元素存在属性  
  41.         if(null != map)  
  42.         {  
  43.             for(int i = 0;i<map.getLength();i++)  
  44.             {  
  45.                 //获得该元素的每一个属性  
  46.                 Attr attr = (Attr)map.item(i);  
  47.                 String attrName = attr.getName();  
  48.                 String attrValue = attr.getValue();  
  49.                 System.out.print(" " + attrName + "=\"" + attrValue + "\"");  
  50.             }  
  51.         }  
  52.         System.out.print(">");  
  53.         for(int i = 0;i < children.getLength();i++)  
  54.         {  
  55.             Node node = children.item(i);  
  56.             //获得节点的类型  
  57.             short nodeType = node.getNodeType();  
  58.             if(nodeType == Node.ELEMENT_NODE)  
  59.             {  
  60.                 //是元素,继续递归  
  61.                 parseElement((Element)node);  
  62.             }  
  63.             else if(nodeType == Node.TEXT_NODE)  
  64.             {  
  65.                 //递归出口  
  66.                 System.out.print(node.getNodeValue());  
  67.             }  
  68.             else if(nodeType == Node.COMMENT_NODE)  
  69.             {  
  70.                 System.out.print("<!--");  
  71.                 Comment comment = (Comment)node;  
  72.                   
  73.                 //注释内容  
  74.                 String data = comment.getData();  
  75.                 System.out.print(data);  
  76.                 System.out.print("-->");  
  77.             }  
  78.         }  
  79.         System.out.print("</" + tagName +">");  
  80.     }  
  81. }  


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值