簡介: XML (eXtensible Markup Language) 意為可擴充套件標記語言,它已經是軟體開發行業中大多數程式設計師和廠商用以選擇作為資料傳輸的載體。本文作者對於 Java 處理 XML 的幾種主流技術進行一些總結和介紹,希望幫助那些有不同需求的開發人員對於 XML 處理技術的作出最優的選擇。
最初,XML 語言僅僅是意圖用來作為 HTML 語言的替代品而出現的,但是隨著該語言的不斷髮展和完善,人們越來越發現它所具有的優點:例如標記語言可擴充套件,嚴格的語法規定,可使用有意義的標記,內容儲存和表現分離等等優勢註定了該語言從誕生之日起就會走向輝煌。 XML 語言在成為 W3C 標準之後進入到了一個快速發展的時期,當然它本身所具有的一系列優點和優勢也註定了各大技術廠商對它的偏愛,Java 作為軟體行業的一種開發技術也迅速作出了反應,出現了多種對 XML 支援的工具,本文將會從這個角度對 Java 處理 XML 的幾種主流技術進行介紹,希望能對您有所幫助。在這篇文章中,您將會得到以下資訊:
1、Java 提供了哪些優秀的類庫及工具便於程式設計師對 XML 進行處理 ?
2、有了 DOM 了,其它工具類庫還有必要麼 ?
3、幾個小例程帶你快速瞭解這三種解析方式
Java 有哪些優秀的類庫及工具便於程式設計師對 XML 進行處理 ?
●大名鼎鼎的 DOM
●綠色環保的 SAX
●默默無聞的 Digester
XML 三種解析方式簡介
大名鼎鼎的 DOM
說它大名鼎鼎可是一點不為過,DOM 是 W3C 處理 XML 的標準 API,它是許多其它與 XML 處理相關的標準的基礎,不僅是 Java,其它諸如 Javascript,PHP,MS .NET 等等語言都實現了該標準, 成為了應用最為廣泛的 XML 處理方式。當然,為了能提供更多更加強大的功能,Java 對於 DOM 直接擴充套件工具類有很多,比如很多 Java 程式設計師耳熟能詳的 JDOM,DOM4J 等等, 它們基本上屬於對 DOM 介面功能的擴充,保留了很多 DOM API 的特性,許多原本的 DOM 程式設計師甚至都沒有任何障礙就熟練掌握了另外兩者的使用,直觀、易於操作的方式使它深受廣大 Java 程式設計師的喜愛。
綠色環保的 SAX
SAX 的應運而生有它特殊的需要,為什麼說它綠色環保呢,這是因為 SAX 使用了最少的系統資源和最快速的解析方式對 XML 處理提供了支援。 但隨之而來繁瑣的查詢方式也給廣大程式設計師帶來許多困擾,常常令人頭痛不已,同時它對 XPath 查詢功能的支援,令人們對它又愛又恨。
默默無聞的 Digester:XML 的 JavaBean 化
Digester 是 apache 基金組織下的一個開源專案,筆者對它的瞭解源於對 Struts 框架的研究,是否有很多程式設計師想要一解各大開源框架的設計甚至想要自己寫一個功能強大的框架時會碰到這樣一個難題: 這些形形色色的用 XML 語言標記的框架配置檔案,框架底層是用什麼技術來解析呢? DOM 解析耗費時間,SAX 解析又過於繁瑣,況且每次解析系統開銷也會過大, 於是,大家想到需要用與 XML 結構相對應的 JavaBean 來裝載這些資訊,由此 Digester 應運而生。它的出現為 XML 轉換為 JavaBean 物件的需求帶來了方便的操作介面,使得更多的類似需求得到了比較完美的
解決方法, 不再需要程式設計師自己實現此類繁瑣的解析程式了。與此同時 SUN 也推出了 XML 和 JavaBean 轉換工具類 JAXB,有興趣的讀者可以自行了解。
三種解析方式比較
DOM
優缺點:實現 W3C 標準,有多種程式語言支援這種解析方式,並且這種方法本身操作上簡單快捷,十分易於初學者掌握。其處理方式是將 XML 整個作為類似樹結構的方式讀入記憶體中以便操作及解析,因此支援應用程式對 XML 資料的內容和結構進行修改,但是同時由於其需要在處理開始時將整個 XML 檔案讀入到記憶體中去進行分析,因此其在解析大資料量的 XML 檔案時會遇到類似於記憶體洩露以及程式崩潰的風險,請對這點多加註意。
適用範圍:小型 XML 檔案解析、需要全解析或者大部分解析 XML、需要修改 XML 樹內容以生成自己的物件模型
SAX
SAX 從根本上解決了 DOM 在解析 XML 文件時產生的佔用大量資源的問題。其實現是通過類似於流解析的技術,通讀整個 XML 文件樹,通過事件處理器來響應程式設計師對於 XML 資料解析的需求。由於其不需要將整個 XML 文件讀入記憶體當中,它對系統資源的節省是十分顯而易見的,它在一些需要處理大型 XML 文件以及效能要求較高的場合有起了十分重要的作用。支援 XPath 查詢的 SAX 使得開發人員更加靈活,處理起 XML 來更加的得心應手。但是同時,其仍然有一些不足之處也困擾廣大的開發人員:首先是它十分複雜的 API 介面令人望而生畏,其次由於其是屬於類似流解析的檔案掃描方式,因此不支援應用程式對於 XML 樹內容結構等的修改,可能會有不便之處。
適用範圍:大型 XML 檔案解析、只需要部分解析或者只想取得部分 XML 樹內容、有 XPath 查詢需求、有自己生成特定 XML 樹物件模型的需求
Digester/JAXB
優缺點 : 由於其是在上述兩者的基礎上衍生出來的工具類,為的是滿足將 XML 轉換為 JavaBean 的特殊需求,故而沒有什麼特別明顯的優缺點。作為大名鼎鼎的開源框架 Struts 的 XML 解析工具 Digester,為我們帶來了將 XML 轉換為 JavaBean 的可靠方法。
適用範圍 : 有將 XML 文件直接轉換為 JavaBean 需求。
應用示例
下面給出一段用於解析的 XML 片段:
清單 1. XML 片段
1 2 3 4 5 6 7 8 9 10 11 |
<?xml version="1.0" encoding="UTF-8"?> <books> <book id="001"> <title>Harry Potter</title> <author>J K. Rowling</author> </book> <book id="002"> <title>Learning XML</title> <author>Erik T. Ray</author> </book> </books> |
DOM 解析 XML
Java 中的 DOM 介面簡介: JDK 中的 DOM API 遵循 W3C DOM 規範,其中 org.w3c.dom 包提供了 Document、DocumentType、Node、
NodeList、Element 等介面, 這些介面均是訪問 DOM 文件所必須的。我們可以利用這些介面建立、遍歷、修改 DOM 文件。
javax.xml.parsers 包中的 DoumentBuilder 和 DocumentBuilderFactory 用於解析 XML 文件生成對應的 DOM Document 物件。
javax.xml.transform.dom 和 javax.xml.transform.stream 包中 DOMSource 類和 StreamSource 類,用於將更新後的 DOM 文件寫入 XML 檔案。
下面給出一個運用 DOM 解析 XML 的例子:
清單 2. DOM 解析 XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
import java.io.File; import java.io.IOException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; public class DOMParser { DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); //Load and parse XML file into DOM public Document parse(String filePath) { Document document = null; try { //DOM parser instance DocumentBuilder builder = builderFactory.newDocumentBuilder(); //parse an XML file into a DOM tree document = builder.parse(new File(filePath)); } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return document; } public static void main(String[] args) { DOMParser parser = new DOMParser(); Document document = parser.parse("books.xml"); //get root element Element rootElement = document.getDocumentElement(); //traverse child elements NodeList nodes = rootElement.getChildNodes(); for (int i=0; i < nodes.getLength(); i++) { Node node = nodes.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) { Element child = (Element) node; //process child element } } NodeList nodeList = rootElement.getElementsByTagName("book"); if(nodeList != null) { for (int i = 0 ; i < nodeList.getLength(); i++) { Element element = (Element)nodeList.item(i); String id = element.getAttribute("id"); } } } } |
在上面的例子中,DOMParser 的 Parse() 方法負責解析 XML 檔案並生成對應的 DOM Document 物件。其中 DocumentBuilderFactory 用於生成 DOM 文件解析器以便解析 XML 文件。 在獲取了 XML 檔案對應的 Document 物件之後,我們可以呼叫一系列的 API 方便的對文件物件模型中的元素進行訪問和處理。 需要注意的是呼叫 Element 物件的 getChildNodes() 方法時將返回其下所有的子節點,其中包括空白節點,因此需要在處理子 Element 之前對節點型別加以判斷。
可以看出 DOM 解析 XML 易於開發,只需要通過解析器建立起 XML 對應的 DOM 樹型結構後便可以方便的使用 API 對節點進行訪問和處理,支援節點的刪除和修改等。 但是 DOM 解析 XML 檔案時會將整個 XML 檔案的內容解析成樹型結構存放在記憶體中,因此不適合用 DOM 解析很大的 XML 檔案。
SAX 解析 XML
與 DOM 建立樹形結構的方式不同,SAX 採用事件模型來解析 XML 文件,是解析 XML 文件的一種更快速、更輕量的方法。 利用 SAX 可以對 XML 文件進行有選擇的解析和訪問,而不必像 DOM 那樣載入整個文件,因此它對記憶體的要求較低。 但 SAX 對 XML 文件的解析為一次性讀取,不建立任何文件物件,很難同時訪問文件中的多處資料。
下面是一個 SAX 解析 XML 的例子:
清單 3. SAX 解析 XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.XMLReaderFactory; public class SAXParser { class BookHandler extends DefaultHandler { private List nameList; private boolean title = false; public ListgetNameList() { return nameList; } // Called at start of an XML document @Override public void startDocument() throws SAXException { System.out.println("Start parsing document..."); nameList = new ArrayList(); } // Called at end of an XML document @Override public void endDocument() throws SAXException { System.out.println("End"); } /** * Start processing of an element. * @param namespaceURI Namespace URI * @param localName The local name, without prefix * @param qName The qualified name, with prefix * @param atts The attributes of the element */ @Override public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { // Using qualified name because we are not using xmlns prefixes here. if (qName.equals("title")) { title = true; } } @Override public void endElement(String namespaceURI, String localName, String qName) throws SAXException { // End of processing current element if (title) { title = false; } } @Override public void characters(char[] ch, int start, int length) { // Processing character data inside an element if (title) { String bookTitle = new String(ch, start, length); System.out.println("Book title: " + bookTitle); nameList.add(bookTitle); } } } public static void main(String[] args) throws SAXException, IOException { XMLReader parser = XMLReaderFactory.createXMLReader(); BookHandler bookHandler = (new SAXParser()).new BookHandler(); parser.setContentHandler(bookHandler); parser.parse("books.xml"); System.out.println(bookHandler.getNameList()); } } |
SAX 解析器介面和事件處理器介面定義在 org.xml.sax 包中。主要的介面包括 ContentHandler、DTDHandler、EntityResolver 及 ErrorHandler。 其中 ContentHandler 是主要的處理器介面,用於處理基本的文件解析事件;DTDHandler 和 EntityResolver 介面用於處理與 DTD 驗證和實體解析相關的事件; ErrorHandler 是基本的錯誤處理介面。DefaultHandler 類實現了上述四個事件處理介面。上面的例子中 BookHandler 繼承了 DefaultHandler 類, 並覆蓋了其中的五個回撥方法 startDocument()、endDocument()、startElement()、endElement() 及 characters() 以加入自己的事件處理邏輯。
Digester 解析 XML
為了滿足將 XML 轉換為 JavaBean 的特殊需求,Apache 旗下的一個名為 Digester 的工具為我們提供了這麼一個選擇。由於最終是將 XML 轉化為 JavaBean 儲存在記憶體當中, 故而解析效能等方面其實與使用者並沒有多大關係。解析的關鍵在於用以匹配 XML 的模式以及規則等,由於該工具較為複雜,限於篇幅,作者只能給予簡單的介紹。
下面是一個 Digester 解析 XML 的例子片段:
清單 4. Digester 解析 XML
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// 定義要解析的 XML 的路徑,並初始化工具類 File input = new File("books.xml"); Digester digester = new Digester(); // 如果碰到了 這個標籤,應該初始化 test.myBean.Books 這個 JavaBean 並填裝相關內容 digester.addObjectCreate("books", "test.myBean.Books"); digester.addSetProperties("books"); // 如果碰到了 這個標籤,同上初始化 test.myBean.Book 這個 JavaBean digester.addObjectCreate("books/book", "test.myBean.Book"); digester.addSetProperties("books/book"); // 通過呼叫上面已經初始化過的 JavaBean 的 addBook() 方法來把多個 加到一個集合中 digester.addSetNext("books/book", "addBook", "test.myBean.Book"); // 定義好了上面的解析規則後,就可以開始進行解析工作了 Books books = (Books) digester.parse(input); |
上述程式碼簡單的向讀者展示了 Digester 處理 XML 的一些要點,主要是說明了一些模式以及規則的匹配。 簡言之,Digester 就是一種用來把一個 XML 轉化為一個與該 XML 結構類似的 JavaBean。你可以把 XML 根元素想象成一個 JavaBean, 該根元素的 attribute 就是這個 JavaBean 的各種 Field,當該根元素有其他子 tag 時,又要把這個子 tag 想象成一個個新的 XML,將其視為一個新的 JavaBean, 並作為一個 Field 加入到父 Bean 當中,然後以此類推,通過迴圈的方式將整個 XML 進行解析。
結束語
本文介紹了 Java 解析 XML 的三種常用技術,其中 DOM 易於上手,程式易於理解,但缺點在於佔用記憶體大,不適合於解析較大的 XML 檔案; SAX 基於事件模型佔用系統資源少,能夠勝任較大的 XML 檔案解析,但解析過程較為繁瑣查詢元素不方便; Digester/JAXB 基於上述兩種技術衍生而來。文中的例項向讀者展示了三種 API 的基本使用方法, 在實際開發過程中使用那種技術解析 XML 更好要依據各自的優缺點視具體情況而定。