(五)使用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>[email protected]</email>  
  6. </person>  
  7. <person sex = "male">  
  8. <name>李四</name>  
  9. <email>[email protected]</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>[email protected]</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>[email protected]</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>[email protected]</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>[email protected]</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. }  


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章