要處理XML文檔,就要先解析(parse)它。解析器是這樣一個程式:它讀入一個檔案,确認這個檔案具有正确的格式,然後将其分解成各種元素,使得程式員能夠通路這些元素。Java庫提供了兩種XML解析器:
像文檔對象模型(Document Object Model, DOM)解析器這樣的樹型解析器(tree parser),它們将讀入的XML文檔轉換成樹結構。
像XML簡單API(Simple API for XML, SAX)解析器這樣的流機制解析器(streaming parser),它們在讀入XML文檔時生成相應的事件。
DOM解析器對于實作我們的大多數目的來說都更容易一些,是以我們首先介紹它。如果你要處理很長的文檔,用它生成樹結構将會消耗大量記憶體,或者如果你隻是對于某些元素感興趣,而不關心它們的上下文,那麼在這些情況下你應該考慮使用流機制解析器。更多的資訊可以檢視3.6節。
DOM解析器的接口已經被W3C标準化了。org.w3c.dom包中包含了這些接口類型的定義,比如:Document和Element等。不同的提供者,比如Apache組織和IBM,都編寫了實作這些接口的DOM解析器。Java XML處理API (Java API for XML Processing, JAXP)庫使得我們實際上可以以插件形式使用這些解析器中的任意一個。但是JDK中也包含了從Apache解析器導出的DOM解析器。
要讀入一個XML文檔,首先需要一個DocumentBuilder對象,可以從DocumentBuilder Factory中得到這個對象,例如:

注意:如果使用輸入流作為輸入源,那麼對于那些以該文檔的位置為相對路徑而被引用的文檔,解析器将無法定位,比如在同一個目錄中的DTD。但是,可以通過安裝一個“實體解析器”(entity resolver)來解決這個問題。請檢視www.xml.com/pub/a/2004/03/03/catalogs.html或www.ibm.com/developerworks/xml/library/x-mxd3.html,以了解更多資訊。
Document對象是XML文檔的樹型結構在記憶體中的表示方式,它由實作了Node接口及其各種子接口的類的對象構成。圖3-1顯示了各個子接口的層次結構。
可以通過調用getDocumentElement方法來啟動對文檔内容的分析,它将傳回根元素。
那麼,調用getDocumentElement方法可以傳回font元素。getTagName方法可以傳回元素的标簽名。在前面這個例子中,root.getTagName()傳回字元串"font"。
如果要得到該元素的子元素(可能是子元素、文本、注釋或其他節點),請使用getChildNodes方法,這個方法會傳回一個類型為NodeList的集合。這個類型在标準的Java集合類建立之前就已經被标準化了,是以它具有一種不同的通路協定;item方法将得到指定索引值的項;getLength方法則提供了項的總數。是以,我們可以像下面這樣枚舉所有子元素:
圖3-2顯示了其DOM樹。
如果隻希望得到子元素,那麼可以忽略空白字元:
現在,隻會看到兩個元素,它們的标簽名是name和size。
正如将在下一節中所看到的那樣,如果你的文檔有DTD,那麼你就可以做得更好。這時,解析器知道哪些元素沒有文本節點的子元素,而且它會幫你剔除空白字元。
在分析name和size元素時,你肯定想擷取它們包含的文本字元串。這些文本字元串本身都包含在Text類型的子節點中。既然知道了這些Text節點是唯一的子元素,就可以用getFirstChild方法而不用再周遊另一個NodeList。然後可以用getData方法擷取存儲在Text節點中的字元串。
提示:對getData的傳回值調用trim方法是個好主意。如果XML檔案的作者将起始和結束的标簽放在不同的行上,例如:
那麼,解析器将會把所有的換行符和空格都包含到文本節點中去。調用trim方法可以把位于實際資料前後的空白字元删掉。
也可以用getLastChild方法得到最後一項子元素,用getNextSibling得到下一個兄弟節點。這樣,另一種周遊子節點集的方法就是:
如果要枚舉節點的屬性,可以調用getAttributes方法。它傳回一個NamedNodeMap對象,其中包含了描述屬性的Node對象。可以用和周遊NodeList一樣的方式在NamedNodeMap中周遊各子節點。然後,調用getNodeName和getNodeValue方法可以得到屬性名和屬性值。
或者,如果知道屬性名,則可以直接擷取相應的屬性值:
現在你已經知道怎麼分析DOM樹了。程式清單3-1中的程式将這些技術都運用了一遍。你可以使用File -> Open菜單選項來讀入一個XML檔案。DocumentBuilder對象會解析這個XML檔案,并産生一個Document對象。該程式會将Document對象顯示為一個JTree(參見圖3-3)。
該樹形結構清楚地顯示了子元素是怎樣被包含空白字元和注釋的文本包圍起來的。為了更清楚起見,這個程式将換行和回車字元顯示為n和r。(否則,它們将顯示為空框,這是Swing對字元串中不能繪制的字元顯示的預設符号)。
在第10章你将會學習到該程式中用來顯示樹形結構和屬性表的技術。DOMTreeModel類實作了TreeModel接口。getRoot方法會傳回文檔的根元素,getChild方法可以得到子元素的節點清單,傳回被請求的索引值對應的項。表的單元格渲染器顯示了以下内容:
對元素,顯示的是元素标簽名和由所有的屬性構成的一張表。
對字元資料,顯示的是接口(Text、Comment、CDATASection),後面跟着資料,其中換行和回車字元被n和r取代。
對其他所有的節點類型,顯示的是類名,後面跟着toString的結果。
程式清單3-1 dom/Treeviewer.java