XML 的四種解析方式html
在上一篇博客中,咱們介紹了什麼是 XML ,http://www.cnblogs.com/ysocean/p/6901008.html,那麼這一篇博客咱們介紹如何來解析 XML 。java
部分文檔引用:http://www.cnblogs.com/lanxuezaipiao/archive/2013/05/17/3082949.html編程
解析 XML 文件通常來講有兩種底層形式,一種是基於樹的結構來解析的稱爲DOM;另外一種是基於事件流的形式稱爲Sax。而在這兩種解析方式的基礎上,基於底層api的更高級封裝解析器也應用而生,好比面向Java的 JDom和 Dom4J。api
一、DOM(Document Object Model)app
DOM 是用與平臺和語言無關的方式表示 XML 文檔的官方 W3C 標準。DOM 是以層次結構組織的節點或信息片段的集合。這個層次結構容許開發人員在樹中尋找特定信息。分析該結構一般須要加載整個文檔和構造層次結構,而後才能作任何工做。因爲它是基於信息層次的,於是 DOM 被認爲是基於樹或基於對象的。dom
優勢:編程語言
①、整個 Dom 樹都加載到內存中了,因此容許隨機讀取訪問數據。ide
②、容許隨機的對文檔結構進行增刪。函數
缺點:性能
①、整個 XML 文檔必須一次性解析完,耗時。
②、整個 Dom 樹都要加載到內存中,佔內存。
適用於:文檔較小,且須要修改文檔內容
二、Sax(Simple API for XML)
SAX處理的特色是基於事件流的。分析可以當即開始,而不是等待全部的數據被處理。並且,因爲應用程序只是在讀取數據時檢查數據,所以不須要將數據存儲在內存中。這對於大型文檔來講是個巨大的優勢。事實上,應用程序甚至沒必要解析整個文檔;它能夠在某個條件獲得知足時中止解析。sax分析器在對xml文檔進行分析時,觸發一系列的事件,應用程序經過事件處理函數實現對xml文檔的訪問,由於事件觸發是有時序性的,因此sax分析器提供的是一種對xml文檔的順序訪問機制,對於已經分析過的部分,不能再從新倒回去處理.此外,它也不能同時訪問處理2個tag,sax分析器在實現時,只是順序地檢查xml文檔中的字節流,判斷當前字節是xml語法中的哪一部分,檢查是否符合xml語法而且觸發相應的事件.對於事件處理函數的自己,要由應用程序本身來實現. SAX解析器採用了基於事件的模型,它在解析XML文檔的時候能夠觸發一系列的事件,當發現給定的tag的時候,它能夠激活一個回調方法,告訴該方法制定的標籤已經找到。
優勢:
①、訪問可以當即進行,不須要等待全部數據被加載。
②、只在讀取數據時檢查數據,不須要保存在內存中
③、不須要將整個數據都加載到內存中,佔用內存少
④、容許註冊多個Handler,能夠用來解析文檔內容,DTD約束等等。
缺點:
①、須要應用程序本身負責TAG的處理邏輯(例如維護父/子關係等),文檔越複雜程序就越複雜。
②、單向導航,沒法定位文檔層次,很難同時訪問同一文檔的不一樣部分數據,不支持XPath。
③、不能隨機訪問 xml 文檔,不支持原地修改xml。
適用於:文檔較大,只須要讀取文檔數據。
三、JDOM(Java-based Document Object Model)
JDOM是處理xml的純java api.使用具體類而不是接口。JDOM具備樹的遍歷,又有SAX的java規則。
JDOM與DOM主要有兩方面不一樣。首先,JDOM僅使用具體類而不使用接口。這在某些方面簡化了API,可是也限制了靈活性。第二,API大量使用了Collections類,簡化了那些已經熟悉這些類的Java開發者的使用。
JDOM自身不包含解析器。它一般使用SAX2解析器來解析和驗證輸入XML文檔(儘管它還能夠將之前構造的DOM表示做爲輸入)。它包含一些轉換器以將JDOM表示輸出成SAX2事件流、DOM模型或XML文本文檔。JDOM是在Apache許可證變體下發布的開放源碼。
優勢:
①、使用具體類而不是接口,簡化了DOM的API。
②、大量使用了Java集合類,方便了Java開發人員。
缺點:
①、不能處理大於內存的文檔.
②、API 簡單,沒有較好的靈活性
四、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複雜,理解難。
②、移植性差。
注:XPath是一門在 XML 文檔中查找信息的語言。
比較:
一、 DOM4J性能最好,連Sun的JAXM也在用DOM4J。目前許多開源項目中大量採用DOM4J,例如大名鼎鼎的Hibernate也用DOM4J來讀取XML配置文件。若是不考慮可移植性,那就採用DOM4J。
二、JDOM和DOM在性能測試時表現不佳,在測試10M文檔時內存溢出,但可移植。在小文檔狀況下還值得考慮使用DOM和JDOM.雖然JDOM的開發者已經說明他們指望在正式發行版前專一性能問題,可是從性能觀點來看,它確實沒有值得推薦之處。另外,DOM還是一個很是好的選擇。DOM實現普遍應用於多種編程語言。它仍是許多其它與XML相關的標準的基礎,由於它正式得到W3C推薦(與基於非標準的Java模型相對),因此在某些類型的項目中可能也須要它(如在JavaScript中使用DOM)。
三、SAX表現較好,這要依賴於它特定的解析方式-事件驅動。一個SAX檢測即將到來的XML流,但並無載入到內存(固然當XML流被讀入時,會有部分文檔暫時隱藏在內存中)。
綜上所述:若是XML文檔較大且不考慮移植性問題建議採用DOM4J;若是XML文檔較小則建議採用JDOM;若是須要及時處理而不須要保存數據則考慮SAX。
實例:
第一步:創建一個 student.xml 文件,咱們以這個文件經過上面的四種解析方式來對比分析。
Tom 11 Bob 22 Marry 23
1、DOM 解析(JDK已經自帶jar包,不須要額外導入!)
package com.ys.xml; import java.io.FileOutputStream; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; public class DomParser{ /** * 解析器讀入整個文檔,而後構建一個駐留內存的樹結構, * 該方法返回 Document 對象,而後咱們能夠經過 這個對象來操做文檔 */ public Document getDocument(String fileName) throws Exception{ //1.建立解析工廠 DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); //2.獲得解析器 DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); //3.獲得文檔對象 Document document = dBuilder.parse(fileName); return document; } //讀取xml文檔中的數據 public void read(String fileName) throws Exception{ //獲取 Document 對象 Document document = new DomParser().getDocument(fileName); //獲取的節點 NodeList nameNode = document.getElementsByTagName("name"); //獲取節點的sex屬性 Element element = (Element) document.getElementsByTagName("name").item(0); System.out.println(element.getAttribute("sex"));//xxx for(int i = 0 ; i < nameNode.getLength() ;i++){ System.out.println(nameNode.item(i).getTextContent()); } /**結果爲 * Tom * Bob * Marry */ //獲取文檔的根元素對象 Element rootElementName = document.getDocumentElement(); System.out.println(rootElementName.getNodeName()); //students //獲得根節點 Node root = document.getElementsByTagName(rootElementName.getNodeName()).item(0); list(root); } //打印全部標籤 private void list(Node root) { if(root instanceof Element){ System.out.println(root.getNodeName()); } NodeList list = root.getChildNodes(); for(int i = 0 ; i < list.getLength() ; i++){ Node child = list.item(i); list(child); } } //向 xml 文件中增長節點和屬性 public void add(String fileName) throws Exception{ //獲取 Document 對象 Document document = new DomParser().getDocument(fileName); //建立節點 Element sex = document.createElement("sex"); sex.setTextContent("男"); //把建立的節點添加到第一個標籤上 Element student = (Element) document.getElementsByTagName("student").item(0); student.appendChild(sex); //在中增長屬性 Element name = (Element) document.getElementsByTagName("name").item(0); name.setAttribute("address", "xxx"); //把更新後的內存寫入xml文檔中 TransformerFactory tfFactory = TransformerFactory.newInstance(); Transformer tFormer = tfFactory.newTransformer(); tFormer.transform(new DOMSource(document), new StreamResult(new FileOutputStream("src/student.xml"))); } //向 xml 文件中刪除節點和屬性 public void delete(String fileName) throws Exception{ //獲取 Document 對象 Document document = new DomParser().getDocument(fileName); //獲得要刪除的第一個節點 Element name = (Element) document.getElementsByTagName("name").item(0); //獲得要刪除的第一個節點的父節點 //Element student = (Element) document.getElementsByTagName("student").item(0); //student.removeChild(name); //上面兩步能夠簡寫爲 name.getParentNode().removeChild(name); //在中刪除屬性 name.removeAttribute("address"); //把更新後的內存寫入xml文檔中 TransformerFactory tfFactory = TransformerFactory.newInstance(); Transformer tFormer = tfFactory.newTransformer(); tFormer.transform(new DOMSource(document), new StreamResult(new FileOutputStream("src/student.xml"))); } //向 xml 文件中更新節點和屬性 public void update(String fileName) throws Exception{ //獲取 Document 對象 Document document = new DomParser().getDocument(fileName); //獲得要刪除的第一個節點 Element name = (Element) document.getElementsByTagName("name").item(0); //在中更新屬性爲 name.setAttribute("address", "yyy"); //更新name節點的文字爲VAE,即vae name.setTextContent("vae"); //把更新後的內存寫入xml文檔中 TransformerFactory tfFactory = TransformerFactory.newInstance(); Transformer tFormer = tfFactory.newTransformer(); tFormer.transform(new DOMSource(document), new StreamResult(new FileOutputStream("src/student.xml"))); } }
2、SAX 解析(JDK已經自帶jar包,不須要額外導入!)
package com.ys.xml; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.Locator; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; public class SaxParser{ public static void main(String[] args) throws Exception { //1.建立解析工廠 SAXParserFactory spFactory = SAXParserFactory.newInstance(); //2.獲得解析器 SAXParser sParser = spFactory.newSAXParser(); //3.獲得讀取器 XMLReader xmlReader = sParser.getXMLReader(); //4.設置內容處理器 xmlReader.setContentHandler(new TagDefaultHandler()); //5.讀取 XML 文檔內容 xmlReader.parse("src/student.xml"); } } //第一種方法:繼承接口ContentHandler 獲得 XML 文檔全部內容 class ListHandler implements ContentHandler{ @Override public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { System.out.println(""); } @Override public void characters(char[] ch, int start, int length) throws SAXException { System.out.println(new String(ch,start,length)); } @Override public void endElement(String uri, String localName, String qName) throws SAXException { System.out.println(""); } @Override public void setDocumentLocator(Locator locator) { } @Override public void startDocument() throws SAXException { } @Override public void endDocument() throws SAXException { } @Override public void startPrefixMapping(String prefix, String uri) throws SAXException { } @Override public void endPrefixMapping(String prefix) throws SAXException { } @Override public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { } @Override public void processingInstruction(String target, String data) throws SAXException { } @Override public void skippedEntity(String name) throws SAXException { } } //使用繼承類 DefaultHandler 更好 class TagDefaultHandler extends DefaultHandler{ //當前解析的是什麼標籤 private String currentTag; //想得到第幾個標籤的值 private int tagNumber=0; //當前解析的是第幾個標籤 private int currentNumber=0; @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { currentTag = qName; //當前解析的name 標籤是第幾個 if("name".equals(currentTag)){ currentNumber++; System.out.println(currentNumber); } } @Override public void characters(char[] ch, int start, int length) throws SAXException { //打印全部name標籤的值 if("name".equals(currentTag)){ System.out.println(new String(ch,start,length)); } //想得到 第二個name標籤的值 tagNumber = 2; if("name".equals(currentTag)&¤tNumber==tagNumber){ System.out.println(new String(ch,start,length)); } } @Override public void endElement(String uri, String localName, String qName) throws SAXException { currentTag = null; } }
3、DOM4J 解析
JAR包下載連接:http://pan.baidu.com/s/1b5L9AA 密碼:wg2l
package com.ys.xml; import java.io.File; import java.io.FileOutputStream; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.OutputFormat; import org.dom4j.io.SAXReader; import org.dom4j.io.XMLWriter; import org.junit.Test; public class DOM4JParser { //讀取第二個 @Test public void read() throws Exception{ SAXReader reader = new SAXReader(); Document document = reader.read(new File("src/student.xml")); //獲得根節點 Element root = document.getRootElement(); //獲得第二個節點 Element student = (Element)root.elements("student").get(1); //獲取中間的值 String value = student.element("name").getText(); System.out.println(value);//Bob //獲取中間的sex值 String sexValue = student.element("name").attributeValue("sex"); System.out.println(sexValue);//xxx } //增長節點 @Test public void add() throws Exception{ SAXReader reader = new SAXReader(); Document document = reader.read(new File("src/student.xml")); Element student = document.getRootElement().element("student"); student.addElement("schoolName").setText("湖北"); OutputFormat format = OutputFormat.createPrettyPrint(); format.setEncoding("utf-8"); XMLWriter writer = new XMLWriter(new FileOutputStream("src/student.xml"),format); writer.write(document); writer.close(); } //刪除節點 @Test public void delete() throws Exception{ SAXReader reader = new SAXReader(); Document document = reader.read(new File("src/student.xml")); Element student = (Element)document.getRootElement().elements("student").get(1); student.element("schoolName").setText("湖南"); OutputFormat format = OutputFormat.createPrettyPrint(); format.setEncoding("utf-8"); XMLWriter writer = new XMLWriter(new FileOutputStream("src/student.xml"),format); writer.write(document); writer.close(); } //修改節點 @Test public void update() throws Exception{ SAXReader reader = new SAXReader(); Document document = reader.read(new File("src/student.xml")); Element student = document.getRootElement().element("student"); Element schoolName = student.element("schoolName"); schoolName.getParent().remove(schoolName); OutputFormat format = OutputFormat.createPrettyPrint(); format.setEncoding("utf-8"); XMLWriter writer = new XMLWriter(new FileOutputStream("src/student.xml"),format); writer.write(document); writer.close(); } }
XPath 簡介:
因爲 DOM4J 是支持 XPath,那麼 XPath 是什麼呢?
XPath 是一門在 XML 文檔中查找信息的語言。使用路徑表達式來選取 XML 文檔中的節點或者節點集。這些路徑表達式和咱們在常規的電腦文件系統中看到的表達式很是類似。
咱們以一個例子看一下用法:
package com.ys.xml; import java.io.File; import java.util.List; import org.dom4j.Document; import org.dom4j.Node; import org.dom4j.io.SAXReader; public class XPathParser { public static void main(String[] args) throws Exception { SAXReader reader = new SAXReader(); Document document = reader.read(new File("src/student.xml")); //獲得第一個 name 節點的值 String nameValue = document.selectSingleNode("//name").getText(); System.out.println(nameValue); //Tom //獲得全部 name 節點的值 ListnameValues = document.selectNodes("//name"); for(Node obj : nameValues){ System.out.println(obj.getText());//Tom Bob Marry } } }