一、三種解析的定義。
1.DOM解析
DOM意為Document Object Model,是檔案物件模型,在解析XML檔案的時候,會把整個檔案載入到記憶體。
2.SAX解析
SAX意為 Simple API For XML,是事件驅動模型,在解析的過程中是一步一步的解析,不需要把整個檔案載入到記憶體中。
3.Pull解析
Pull解析是Android官方推薦使用的一種解析方式,本身和Sax解析很相似。
二、三種解析方式的區別,優勢劣勢。
DOM解析
優勢是可以在解析完成後,還可以倒著回退回去解析,因為檔案被載入到記憶體中,無論要解析哪一個節點都是可以的。
劣勢就是解析的時候需要把整個文檔載入到記憶體,從而佔用空間,資源消耗很大。
SAX解析
優勢在於解析速度快,不必要把整個文檔載入到記憶體裡,消耗會比較小,效率比較高
劣勢在於只能夠按照順序解析下來,倒著回退解析是沒有辦法的。
Pull解析
相對於Sax解析的區別在於,貌似是。。。。Sax解析不可以隨時停止,要完全解析完成,pull是在while迴圈解析的,那麼可以自由的添加在什麼時候解析完成。
SAX解析器的工作方式是自動將事件推入註冊的事件處理器進行處理,因此你不能控制事件的處理主動結束;而Pull解析器的工作方式為允許你的應用程式代碼主動從解析器中擷取事件,正因為是主動擷取事件,因此可以在滿足了需要的條件後不再擷取事件,結束解析。這是他們主要的區別。[1]
三、三種解析的代碼實現。
XML檔案
<?xml version="1.0" encoding="UTF-8"?><books> <book id="1"> <name>Developer</name> <price>20.0</price> </book> <book id="2" > <name>Engineer</name> <price>30.0</price> </book></books>
Java代碼
package cn.My.utils;import java.io.IOException;import java.io.InputStream;import java.util.ArrayList;import java.util.List;import javax.xml.parsers.DocumentBuilder;import javax.xml.parsers.DocumentBuilderFactory;import javax.xml.parsers.ParserConfigurationException;import javax.xml.parsers.SAXParser;import javax.xml.parsers.SAXParserFactory;import org.w3c.dom.Document;import org.w3c.dom.Element;import org.w3c.dom.NodeList;import org.xml.sax.Attributes;import org.xml.sax.SAXException;import org.xml.sax.helpers.DefaultHandler;import org.xmlpull.v1.XmlPullParser;import org.xmlpull.v1.XmlPullParserException;import org.xmlpull.v1.XmlPullParserFactory;import cn.My.Model.Books;public class Parser {/** * DOM解析 * * @param is * @return List<Books> */public List<Books> DOM_Parser(InputStream is) {List<Books> data = new ArrayList<Books>();try {DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();DocumentBuilder db = dbf.newDocumentBuilder();Document doc = db.parse(is);//db要解析的檔案流Element root = doc.getDocumentElement();//擷取根節點。NodeList nodeList = root.getElementsByTagName("book");//根節點擷取book節點列表for (int i = 0; i < nodeList.getLength(); i++) {Books book = new Books();Element e = (Element) nodeList.item(i);//每一本書的節點。book.bookId = Integer.valueOf(e.getAttribute("id"));//擷取book屬性book.bookName = e.getElementsByTagName("name").item(0).getFirstChild().getNodeValue();//擷取節點名為name的節點,並且getFirstChild,得到文本節點,getNodeValue是文本節點的值,也就是name的值。(name)book.bookPrice = Float.valueOf(e.getElementsByTagName("price").item(0).getFirstChild().getNodeValue());data.add(book);}} catch (ParserConfigurationException e) {e.printStackTrace();} catch (SAXException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return data;}/** * SAX解析 * @param is * @return */public List<Books> Sax_Parser(InputStream is) {SAXParserFactory sf = SAXParserFactory.newInstance();MyHandler myHandler = new MyHandler();try {SAXParser sp = sf.newSAXParser();sp.parse(is, myHandler);} catch (ParserConfigurationException e) {e.printStackTrace();} catch (SAXException e) {e.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {}return myHandler.getData();}/** * pull解析 * @param is * @return List<Books> */public List<Books> Pull_Parser(InputStream is){List<Books> data =null;Books book = null;try {XmlPullParserFactory xppf = XmlPullParserFactory.newInstance();XmlPullParser xpp = xppf.newPullParser();xpp.setInput(is, "UTF-8");int event = xpp.getEventType();while (event!=XmlPullParser.END_DOCUMENT) {switch (event) {case XmlPullParser.START_DOCUMENT:data = new ArrayList<Books>();break;case XmlPullParser.START_TAG:String str = xpp.getName();if("book".equals(str)){book = new Books();book.bookId = Integer.valueOf(xpp.getAttributeValue(0));}if("name".equals(str)){book.bookName = xpp.nextText();}if("price".equals(str)){book.bookPrice = Float.valueOf(xpp.nextText());}break;case XmlPullParser.END_TAG:if("book".equals(xpp.getName())){data.add(book);book =null;}break;}event = xpp.next();}} catch (XmlPullParserException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (NumberFormatException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}return data;}public class MyHandler extends DefaultHandler {private List<Books> data;private Books book;private String tarName;@Overridepublic void startDocument() throws SAXException {super.startDocument();data = new ArrayList<Books>();}@Overridepublic void startElement(String uri, String localName, String qName,Attributes attributes) throws SAXException {super.startElement(uri, localName, qName, attributes);if ("book".equals(localName)) {book = new Books();book.bookId = Integer.valueOf(attributes.getValue(0));}tarName = localName;}@Overridepublic void characters(char[] ch, int start, int length)throws SAXException {super.characters(ch, start, length);if ("name".equals(tarName)) {book.bookName = new String(ch, start, length);}if ("price".equals(tarName)) {book.bookPrice = Float.valueOf(new String(ch, start, length));}tarName=null;}@Overridepublic void endElement(String uri, String localName, String qName)throws SAXException {if("book".equals(localName)){data.add(book);book = null;}}@Overridepublic void endDocument() throws SAXException {super.endDocument();}public List<Books> getData() {return data;}}}
分析:
DOM解析,把InputStream寫到Document裡面,通過doc.getDocumentElement();擷取根節點,也就是解析到了<books>的時候,
再通過root.getElementByTagName("book");獲得所有節點名為book的節點,返回一個nodelist的對象,也就是說,有多少的book,nodeList的長度就是多少,
所以,在這個時候建立book對象是非常合適的。接下來就是是對每一個book節點對象的繼續擷取元素節點,即name,price。
當然在擷取到值之後都封裝在book對象裡面,然後在迴圈裡面把book添加到data,返回即可。
SAX解析,首先要整合一個DefaultHandler的類,因為整個的解析都是在這個類完成的。
要重寫的方法是
startDocument()
startElement(String uri, String localName, String qName,Attributes attributes)
characters(char[] ch, int start, int length)
endElement(String uri, String localName, String qName)
這4個即可。
startDocument()就是開始解析文檔,這時候,就可以初始化一個List<Books>的列表。因為接下去要在解析過程中要去添加,所以要在這邊做初始化。
startElement,對每一個元素節點進行解析,其中參數localName就是節點名兒,我們需要在開始的時候就建立一個String tagName用來儲存localName的值
當localName的值和"book"一致,那麼這時候就說明解析到了一本書,這時候就可以建立一個book對象了,然後把localname儲存在tagname。把bookId的值,用attribute去獲得即可。
characters就是解析到的文本節點的內容了,剛剛儲存的tagName在這時候進行和“name”,"price"進行比較,如果一致的話,那麼就可以把bookName,bookPrice的值封裝起來了。並且這時候要把tagname賦值為null,否則如果不為空白的時候,第二次到了空白的元素節點,tagName不為空白,又會進行一次-判斷,並且把得到的空白元素節點的值付給了bookName和bookPrice,也就得到了錯誤的解析結果
endElement,判斷,如果tagName的值是book,那麼就代表解析一本book,也就要把book添加到data裡面去。然後把book對象賦值為空白。
Pull解析
比Sax解析爽的一點就是不用寫這個啥子Handler....其實也差不多- -、
要獲得xpp對象,並且要給予流的來源
xpp.SetInput(is,"UTF-8");
這時候需要一個int型的event用來接受節點狀態的,此時把xpp.getEventType獲得到,在While迴圈裡開始解析
只要event != END_DOCUMENT,那就一直解析。是不是很好理解?
這時候switch用來判斷,
如果 event==StartDocument,那麼就建立一個List<Books> 的對象,用於添加book
如果 event == StartElement ,那麼在嵌套if判斷
getName的值等於book,就new一個book對象,值等於name,就把getNextText的值賦值給book.bookName.同理,price也是一樣。
如果event == EndElement ,加if判斷,如果getName的值是book,那麼說明就解析完了一本書,這時候就要把book添加到data裡面,並且賦值為空白。
最後,要記得移動xpp,要不怎麼一直往下解析呢?
所以 event = xpp.next();
附:
關於節點問題
從book開始解析有5個元素節點,book一個,黑色線條的空白是第二個,name整個算時候第三個,第二條黑色線條的空白是第四個,price是第五個
而被夾在name和/name之間的也是一個節點,叫做文本節點,所以這也就是在擷取到了name節點之後,還要getFirstChild的原因,擷取到了FirstChild才是獲得到了文位元組點,再getNodeValue才會得到正確的值。
大概就是這麼多把,作為自己學習完成的筆記,總結才會有更多的收穫,可能語言群組織上有點混亂什麼的,渣渣文筆,如果文中有什麼問題,或者是有什麼好的建議和意見,請留言給我,謝謝。
PS:紅色字型為網路Copy.....
[1] 來源http://blog.csdn.net/leorowe/article/details/6841375