這篇文章是我上網找資料,加上自己總結了一些而得
資料來源:
http://www.cnblogs.com/allenzheng/archive/2012/12/01/2797196.html
http://www.cnblogs.com/lanxuezaipiao/archive/2013/05/17/3082949.html
其實我覺得這四種解析方式又大致可以分為兩種:一種是基於樹結構處理的Dom解析,另外一種是基於事件模型的SAX解析
一、介紹及優缺點分析
1. DOM(Document Object Model)
DOM是用與平台和語言無關的方式表示XML文檔的官方W3C標准。DOM是以層次結構組織的節點或信息片斷的集合。這個層次結構允許開發人員在樹中尋找特定信息。分析該結構通常需要加載整個文檔和構造層次結構,然后才能做任何工作。由於它是基於信息層次的,因而DOM被認為是基於樹或基於對象的。
【優點】
①允許應用程序對數據和結構做出更改。
②訪問是雙向的,可以在任何時候在樹中上下導航,獲取和操作任意部分的數據。
【缺點】
①通常需要加載整個XML文檔來構造層次結構,消耗資源大。
xml:
<?xml version="1.0" encoding="UTF-8"?> <users> <user id="0"> <name>yaobo</name> <age>22</age> <sex>male</sex> </user> <user id="1"> <name>學生</name> <age>24</age> <sex>Male</sex> </user> <user id="2"> <name>wjm</name> <age>23</age> <sex>male</sex> </user> <user id="3"> <name>wh</name> <age>24</age> <sex>Male</sex> </user> <user > <name>yaobo</name> <age>12</age> </user> </users>
Dom解析:
package Test1; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; public class Dom { public void parser(String url){ DocumentBuilderFactory dbf=DocumentBuilderFactory.newInstance(); try { DocumentBuilder db=dbf.newDocumentBuilder(); Document doc=db.parse(url); NodeList users=doc.getChildNodes(); for(int i=0;i<users.getLength();i++){ Node user=users.item(i); //這次取出的是節點users //System.out.println(user.getNodeName()+users.getLength()); NodeList userList=user.getChildNodes(); for(int j=0;j<userList.getLength();j++){ Node info=userList.item(j); //這次取出的是節點user //System.out.println(info.getNodeName()); NodeList attribute=info.getChildNodes(); //System.out.println(attribute.item(0).getNodeName()+":"+attribute.item(0).getTextContent()); for(int k=0;k<attribute.getLength();k++){ if(attribute.item(k).getNodeName() != "#text"){ //這次取出的user下面的屬性節點 System.out.println(attribute.getLength()+attribute.item(k).getNodeName()+":"+attribute.item(k).getTextContent()); } } System.out.println(); } } } catch (Exception e) { e.printStackTrace(); } } }
package Test1; public class Test { public static void main(String[] args) { String url="src/Test/Jixie.xml"; // Sax sax=new Sax(); // sax.parse(); // Dom dom=new Dom(); dom.parser(url); } }
2. SAX(Simple API for XML)
SAX處理的優點非常類似於流媒體的優點。分析能夠立即開始,而不是等待所有的數據被處理。而且,由於應用程序只是在讀取數據時檢查數據,因此不需要將數據存儲在內存中。這對於大型文檔來說是個巨大的優點。事實上,應用程序甚至不必解析整個文檔;它可以在某個條件得到滿足時停止解析。一般來說,SAX還比它的替代者DOM快許多。
選擇DOM還是選擇SAX? 對於需要自己編寫代碼來處理XML文檔的開發人員來說, 選擇DOM還是SAX解析模型是一個非常重要的設計決策。 DOM采用建立樹形結構的方式訪問XML文檔,而SAX采用的是事件模型。
DOM解析器把XML文檔轉化為一個包含其內容的樹,並可以對樹進行遍歷。用DOM解析模型的優點是編程容易,開發人員只需要調用建樹的指令,然后利用navigation APIs訪問所需的樹節點來完成任務。可以很容易的添加和修改樹中的元素。然而由於使用DOM解析器的時候需要處理整個XML文檔,所以對性能和內存的要求比較高,尤其是遇到很大的XML文件的時候。由於它的遍歷能力,DOM解析器常用於XML文檔需要頻繁的改變的服務中。
SAX解析器采用了基於事件的模型,它在解析XML文檔的時候可以觸發一系列的事件,當發現給定的tag的時候,它可以激活一個回調方法,告訴該方法制定的標簽已經找到。SAX對內存的要求通常會比較低,因為它讓開發人員自己來決定所要處理的tag.特別是當開發人員只需要處理文檔中所包含的部分數據時,SAX這種擴展能力得到了更好的體現。但用SAX解析器的時候編碼工作會比較困難,而且很難同時訪問同一個文檔中的多處不同數據。
【優勢】
①不需要等待所有數據都被處理,分析就能立即開始。
②只在讀取數據時檢查數據,不需要保存在內存中。
③可以在某個條件得到滿足時停止解析,不必解析整個文檔。
④效率和性能較高,能解析大於系統內存的文檔。
【缺點】
①需要應用程序自己負責TAG的處理邏輯(例如維護父/子關系等),文檔越復雜程序就越復雜。
②單向導航,無法定位文檔層次,很難同時訪問同一文檔的不同部分數據,不支持XPath。
下面結合一張圖來詳細講解Sax解析。

xml文件被Sax解析器載入,由於Sax解析是按照xml文件的順序來解析,當讀入<?xml.....>時,會調用startDocument()方法,當讀入<books>的時候,由於它是個ElementNode,所以會調用startElement(String uri, String localName, String qName, Attributes attributes) 方法,其中第二個參數就是節點的名稱,注意:由於有些環境不一樣,有時候第二個參數有可能為空,所以可以使用第三個參數,因此在解析前,先調用一下看哪個參數能用,第4個參數是這個節點的屬性。這里我們不需要這個節點,所以從<book>這個節點開始,也就是圖中1的位置,當讀入時,調用startElement(....)方法,由於只有一個屬性id,可以通過attributes.getValue(0)來得到,然后在圖中標明2的地方會調用characters(char[] ch, int start, int length)方法,不要以為那里是空白,Sax解析器可不那么認為,Sax解析器會把它認為是一個TextNode。但是這個空白不是我們想要的數據,我們是想要<name>節點下的文本信息。這就要定義一個記錄當上一節點的名稱的TAG,在characters(.....)方法中,判斷當前節點是不是name,是再取值,才能取到thinking in java。
Sax解析:
package Test1; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; public class Sax { public void parse(String url) { SAXParserFactory sf= SAXParserFactory.newInstance(); SAXParser sp; try { sp = sf.newSAXParser(); MyHander mh=new MyHander("user"); sp.parse(url, mh); } catch (Exception e) { e.printStackTrace(); } } } class MyHander extends DefaultHandler{ String nodeName; Map<String,String> map; //將所以解析的數據存到list集合中 List<Map> list; public MyHander(String nodeName){ this.nodeName=nodeName; list=new ArrayList<Map>(); } //這是 解析到當前節點的名稱 String tagName; @Override public void startDocument() throws SAXException { System.out.println("解析開始:"); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { System.out.println("startElement:"+qName); //判斷是否為所需節點名 if(qName==nodeName){ map=new HashMap<String,String>(); } if(attributes!=null && attributes.getLength()>0){ //這一步是將id的<user id="">中的id值得名稱和屬性存到map中 map.put(attributes.getQName(0), attributes.getValue(0)); } tagName=qName; } @Override public void characters(char[] ch, int start, int length) throws SAXException { System.out.println("characters: "); if(map!=null && tagName!=null){ //獲取當前節點的內容 String content=new String(ch,start,length); if(content!=null && !content.trim().equals("") && !content.trim().equals("\n")){ System.out.println("yaobo"+content); map.put(tagName, content); System.out.println(tagName+" "+content); } content=null; } } @Override public void endElement(String uri, String localName, String qName) throws SAXException { System.out.println("endElement:"+tagName); if(qName.equals(nodeName)){ list.add(map); map=null; } } @Override public void endDocument() throws SAXException { System.out.println("解析結束"); System.out.println(list); } }
- startDocument()方法只會在文檔開始解析的時候被調用,每次解析只會調用一次。
- startElement()方法每次在開始解析一個元素,即遇到元素標簽開始的時候都會調用。
- characters()方法也是在每次解析到元素標簽攜帶的內容時都會調用,即使該元素標簽的內容為空或換行。而且如果元素內嵌套元素,在父元素結束標簽前, characters()方法會再次被調用,此處需要注意。
- endElement()方法每次在結束解析一個元素,即遇到元素標簽結束的時候都會調用。
- endDocument() startDocument()方法只會在文檔解析結束的時候被調用,每次解析只會調用一次。
3. JDOM(Java-based Document Object Model)
JDOM的目的是成為Java特定文檔模型,它簡化與XML的交互並且比使用DOM實現更快。由於是第一個Java特定模型,JDOM一直得到大力推廣和促進。正在考慮通過“Java規范請求JSR-102”將它最終用作“Java標准擴展”。從2000年初就已經開始了JDOM開發。
JDOM與DOM主要有兩方面不同。首先,JDOM僅使用具體類而不使用接口。這在某些方面簡化了API,但是也限制了靈活性。第二,API大量使用了Collections類,簡化了那些已經熟悉這些類的Java開發者的使用。
JDOM文檔聲明其目的是“使用20%(或更少)的精力解決80%(或更多)Java/XML問題”(根據學習曲線假定為20%)。JDOM對於大多數Java/XML應用程序來說當然是有用的,並且大多數開發者發現API比DOM容易理解得多。JDOM還包括對程序行為的相當廣泛檢查以防止用戶做任何在XML中無意義的事。然而,它仍需要您充分理解XML以便做一些超出基本的工作(或者甚至理解某些情況下的錯誤)。這也許是比學習DOM或JDOM接口都更有意義的工作。
JDOM自身不包含解析器。它通常使用SAX2解析器來解析和驗證輸入XML文檔(盡管它還可以將以前構造的DOM表示作為輸入)。它包含一些轉換器以將JDOM表示輸出成SAX2事件流、DOM模型或XML文本文檔。JDOM是在Apache許可證變體下發布的開放源碼。
【優點】
①使用具體類而不是接口,簡化了DOM的API。
②大量使用了Java集合類,方便了Java開發人員。
【缺點】
①沒有較好的靈活性。
②性能較差。
package Test1; import java.io.IOException; import java.util.List; import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.JDOMException; import org.jdom2.input.SAXBuilder; public class JDom { public void parserXml(String fileName){ SAXBuilder sb=new SAXBuilder(); try { Document doc=sb.build(fileName); Element users=doc.getRootElement(); List userList=users.getChildren(); for(int i=0;i<userList.size();i++){ Element user=(Element) userList.get(i); List content=user.getChildren(); for(int j=0;j<content.size();j++){ System.out.println(((Element) content.get(j)).getName()+":"+((Element) content.get(j)).getValue()); } System.out.println(); } } catch (JDOMException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
4. DOM4J(Document Object Model for Java)
雖然DOM4J代表了完全獨立的開發結果,但最初,它是JDOM的一種智能分支。它合並了許多超出基本XML文檔表示的功能,包括集成的XPath支持、XML Schema支持以及用於大文檔或流化文檔的基於事件的處理。它還提供了構建文檔表示的選項,它通過DOM4J API和標准DOM接口具有並行訪問功能。從2000下半年開始,它就一直處於開發之中。
為支持所有這些功能,DOM4J使用接口和抽象基本類方法。DOM4J大量使用了API中的Collections類,但是在許多情況下,它還提供一些替代方法以允許更好的性能或更直接的編碼方法。直接好處是,雖然DOM4J付出了更復雜的API的代價,但是它提供了比JDOM大得多的靈活性。
在添加靈活性、XPath集成和對大文檔處理的目標時,DOM4J的目標與JDOM是一樣的:針對Java開發者的易用性和直觀操作。它還致力於成為比JDOM更完整的解決方案,實現在本質上處理所有Java/XML問題的目標。在完成該目標時,它比JDOM更少強調防止不正確的應用程序行為。
DOM4J是一個非常非常優秀的Java XML API,具有性能優異、功能強大和極端易用使用的特點,同時它也是一個開放源代碼的軟件。如今你可以看到越來越多的Java軟件都在使用DOM4J來讀寫XML,特別值得一提的是連Sun的JAXM也在用DOM4J.
【優點】
①大量使用了Java集合類,方便Java開發人員,同時提供一些提高性能的替代方法。
②支持XPath。
③有很好的性能。
【缺點】
①大量使用了接口,API較為復雜。
package Test1; import java.io.File; import java.util.Iterator; import java.util.List; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; public class Dom4j { public void parserXml(String url){ File inputXml=new File(url); SAXReader sr=new SAXReader(); try { Document doc=sr.read(inputXml); Element users=(Element) doc.getRootElement(); for(Iterator i=users.elementIterator(); i.hasNext();){ Element user=(Element) i.next(); for(Iterator j=user.elementIterator();j.hasNext();){ //System.out.println(((Element) j.next()).getName()+":"+((Element) j.next()).getText()); Element node = (Element) j.next(); System.out.println(node.getName() +":"+node.getText()); //這里不能用下面這種寫法來表示,我覺得可能是讀取速度的問題 // System.out.println((Element) j.next().getName() + ":" + node.getText()); } System.out.println(); } } catch (DocumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
二、比較
1. DOM4J性能最好,連Sun的JAXM也在用DOM4J。目前許多開源項目中大量采用DOM4J,例如大名鼎鼎的Hibernate也用DOM4J來讀取XML配置文件。如果不考慮可移植性,那就采用DOM4J.
2. JDOM和DOM在性能測試時表現不佳,在測試10M文檔時內存溢出,但可移植。在小文檔情況下還值得考慮使用DOM和JDOM.雖然JDOM的開發者已經說明他們期望在正式發行版前專注性能問題,但是從性能觀點來看,它確實沒有值得推薦之處。另外,DOM仍是一個非常好的選擇。DOM實現廣泛應用於多種編程語言。它還是許多其它與XML相關的標准的基礎,因為它正式獲得W3C推薦(與基於非標准的Java模型相對),所以在某些類型的項目中可能也需要它(如在JavaScript中使用DOM)。
3. SAX表現較好,這要依賴於它特定的解析方式-事件驅動。一個SAX檢測即將到來的XML流,但並沒有載入到內存(當然當XML流被讀入時,會有部分文檔暫時隱藏在內存中)。
我的看法:如果XML文檔較大且不考慮移植性問題建議采用DOM4J;如果XML文檔較小則建議采用JDOM;如果需要及時處理而不需要保存數據則考慮SAX。但無論如何,還是那句話:適合自己的才是最好的,如果時間允許,建議大家講這四種方法都嘗試一遍然后選擇一種適合自己的即可。
