解析XML的方式有很多種,大家比較熟悉的可能就是DOM解析。
DOM(檔案物件模型)解析:解析器讀入整個文檔,然後構建一個駐留記憶體的樹結構,然後代碼就可以根據DOM介面來操作這個樹結構了。
優點:整個文檔讀入記憶體,方便操作:支援修改、刪除和重現排列等多種功能。
缺點:將整個文檔讀入記憶體中,保留了過多的不需要的節點,浪費記憶體和空間。
使用場合:一旦讀入文檔,還需要多次對文檔進行操作,並且在硬體資源充足的情況下(記憶體,CPU)。
為瞭解決DOM解析存在的問題,就出現了SAX解析。其特點為:
優點:不用實現調入整個文檔,佔用資源少。尤其在嵌入式環境中,如android,極力推薦使用SAX解析。
缺點:不像DOM解析一樣將文檔長期駐留在記憶體中,資料不是持久的。如果事件過後沒有儲存資料,資料就會丟失。
使用場合:機器有效能限制。
SAX解析XML文檔採用事件驅動模式。什麼是事件驅動模式?它將XML文檔轉換成一系列的事件,由單獨的事件處理器來決定如何處理。
基於事件驅動的處理模式主要是基於事件來源和事件處理器(或者叫監聽器)來工作的。一個可以產生事件的對象叫做事件來源,而一個可以針對事件做出響應的對象就被叫做事件處理器。
在SAX介面中,事件來源是org.xml.sax包中的XMLReader,他通過parse()方法開始解析XML文檔,並根據文檔內容產生事件。而事件處理器則是org.xml.sax包中的ContentHandler、DTDHandler、ErrorHandler,以及EntityResolver這四個介面。他們分別處理事件來源在解析過程中產生不同類的事件(其中DTDHandler為解析文檔DTD時所用)。詳細介紹如下表
在上述四個介面中,最重要的就是ContentHandler這個介面,下面是對這個介面方法的說明:
//設定一個可以定位文檔內容事件發生位置的定位器對象public void setDocumentLocator(Locator locator)//用於處理文檔解析開始事件public void startDocument()throws SAXException//處理元素開始事件,從參數中可以獲得元素所在名稱空間的uri,元素名稱,屬性類表等資訊public void startElement(String namespacesURI , String localName , String qName , Attributes atts) throws SAXException//處理元素結束事件,從參數中可以獲得元素所在名稱空間的uri,元素名稱等資訊public void endElement(String namespacesURI , String localName , String qName) throws SAXException//處理元素的字元內容,從參數中可以獲得內容public void characters(char[] ch , int start , int length) throws SAXException
這裡再介紹下XMLReader中的方法。
//註冊處理XML文檔解析事件ContentHandlerpublic void setContentHandler(ContentHandler handler)//開始解析一個XML文檔public void parse(InputSorce input) throws SAXException
SAX實現實體解析的步驟:
在android中使用SAX是有跡可循的,完全可以按照下面的方法就可以輕鬆找到xml裡的tag,然後得到想要的內容。具體實現步驟如下:
(一)第一步:建立一個工廠類SAXParserFactory,代碼如下:
SAXParserFactory factory = SAXParserFactory.newInstance();
(二)第二步:讓工廠類產生一個SAX的解析類SAXParser,代碼如下:
SAXParser parser = factory.newSAXParser();
(三)第三步:從SAXPsrser中得到一個XMLReader執行個體,代碼如下:
XMLReader reader = parser.getXMLReader();
(四)第四步:把自己寫的handler註冊到XMLReader中,一般最重要的就是ContentHandler,代碼如下:
RSSHandler handler = new RSSHandler();reader.setContentHandler(handler);
(五)第五步:將一個xml文檔或者資源變成一個java可以處理的InputStream流後,解析正式開始,代碼如下:
parser.parse(is);
上面幾個步驟中,最重要、最關鍵的就是第四步,handler的實現。
下面是XML檔案:
<?xml version="1.0" encoding="ISO-8859-1"?><resources><resource><id>0001</id><mp3.name>m1.mp3</mp3.name><mp3.size>5746816</mp3.size><lrc.name>m1.lrc</lrc.name><lrc.size>1778</lrc.size></resource><resource><id>0002</id> <mp3.name>m2.mp3</mp3.name><mp3.size>5420198</mp3.size><lrc.name>m2.lrc</lrc.name><lrc.size>1598</lrc.size></resource></resources>
添加一個實體類,這樣我們就可以把解析出來的資訊放到實體類裡面,然後直接操作實體類就可以了
Mp3Info.java
package android.model;public class Mp3Info {private String id;private String mp3Name;private String mp3Size;private String lrcName;private String lrcSize;public Mp3Info() {super();}public Mp3Info(String id, String mp3Name, String mp3Size, String lrcName,String lrcSize) {super();this.id = id;this.mp3Name = mp3Name;this.mp3Size = mp3Size;this.lrcName = lrcName;this.lrcSize = lrcSize;}@Overridepublic String toString() {return "Mp3Info [id=" + id + ", mp3Name=" + mp3Name + ", mp3Size="+ mp3Size + ", lrcName=" + lrcName + ", lrcSize=" + lrcSize+ "]";}public String getId() {return id;}public void setId(String id) {this.id = id;}public String getMp3Name() {return mp3Name;}public void setMp3Name(String mp3Name) {this.mp3Name = mp3Name;}public String getMp3Size() {return mp3Size;}public void setMp3Size(String mp3Size) {this.mp3Size = mp3Size;}public String getLrcName() {return lrcName;}public void setLrcName(String lrcName) {this.lrcName = lrcName;}public String getLrcSize() {return lrcSize;}public void setLrcSize(String lrcSize) {this.lrcSize = lrcSize;}}下面就是最最重要的地方了,建立自己的ContentHandler.看下面的代碼:
Mp3ListContentHandler:
package android.xml;import java.util.List;import org.xml.sax.Attributes;import org.xml.sax.SAXException;import org.xml.sax.helpers.DefaultHandler;import android.model.Mp3Info;public class Mp3ListContentHandler extends DefaultHandler{private List<Mp3Info> infos=null;private Mp3Info mp3Info=null;private String tagName=null;public Mp3ListContentHandler(List<Mp3Info> infos) {super();this.infos = infos;}public List<Mp3Info> getInfos() {return infos;}public void setInfos(List<Mp3Info> infos) {this.infos = infos;}@Overridepublic void characters(char[] ch, int start, int length)throws SAXException {//System.out.println("-------characters------");String temp=new String(ch, start, length);if(tagName.equals("id")){mp3Info.setId(temp);}else if(tagName.equals("mp3.name")){mp3Info.setMp3Name(temp);}else if(tagName.equals("mp3.size")){mp3Info.setMp3Size(temp);}else if(tagName.equals("lrc.name")){mp3Info.setLrcName(temp);}else if(tagName.equals("lrc.size")){mp3Info.setLrcSize(temp);}}@Overridepublic void endDocument() throws SAXException {//System.out.println("------endDocument------");}@Overridepublic void endElement(String uri, String localName, String qName)throws SAXException {//System.out.println("---------endElement----------");if(qName.equals("resource")){infos.add(mp3Info);}tagName="";}@Overridepublic void startDocument() throws SAXException {// TODO Auto-generated method stubsuper.startDocument();//System.out.println("---------startDocument--------");}@Overridepublic void startElement(String uri, String localName, String qName,Attributes attributes) throws SAXException {this.tagName=localName;//System.out.println("--------startElement-------");if(tagName.equals("resource")){mp3Info=new Mp3Info();}}}就上面的程式碼分析,實現一個ContentHandler一般要一下幾個步驟:
1、聲明一個類,繼承DefaultHandler。DefaultHandler是一個基類,這個類裡面簡單實現了一個ContentHandler。我們只需要重寫裡面的方法即可。
2、重寫 startDocument() 和 endDocument(),一般解析將正式解析之前的一些初始化工資放到startDocument()裡面,收尾的工作放到endDocument()裡面。
3、重寫startElement(),XML解析器遇到XML裡面的tag時就會調用這個函數。經常在這個函數內是通過localName倆進行判斷而操作一些資料。
4、重寫characters()方法,這是一個回調方法。解析器執行完startElement()後,解析完節點的內容後就會執行這個方法,並且參數ch[]就是節點的內容。
5、重寫endElement()方法,這個方法與startElement()相對應,解析完一個tag節點後,執行這個方法。再找個例子中,如果解析一個item結束,就將RSSIiem添加到RSSFeed中
最後我們實現一個activity來展現解析的結果:
package android.test;import java.io.StringReader;import java.util.ArrayList;import java.util.Iterator;import java.util.List;import javax.xml.parsers.SAXParserFactory;import org.xml.sax.InputSource;import org.xml.sax.XMLReader;import android.app.ListActivity;import android.download.HttpDownloader;import android.model.Mp3Info;import android.os.Bundle;import android.view.Menu;import android.view.MenuItem;import android.xml.Mp3ListContentHandler;public class Mp3ListActivity extends ListActivity {private static final int UPDATE=1;private static final int ABOUT=2;/* * 當使用者點擊MENU按鈕時,調用該方法,可以在這個方法中放入自己的控制項 */@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// TODO Auto-generated method stubmenu.add(0, UPDATE, 1, R.string.mp3list_update);menu.add(0, ABOUT, 2, R.string.mp3list_about);return super.onCreateOptionsMenu(menu);}@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); }//處理按鈕的函數,當使用者點擊menu彈出的按鈕時會觸發此函數 @Overridepublic boolean onOptionsItemSelected(MenuItem item) {// TODO Auto-generated method stub //System.out.println("item id------>"+item.getItemId());if(item.getItemId()==UPDATE){//使用者點擊了更新列表按鈕String xml=downloadXML("http://172.23.10.234:8080/mp3/resources.xml");parse(xml);}else if(item.getItemId()==ABOUT){//使用者點擊了關於按鈕} return super.onOptionsItemSelected(item);} public String downloadXML(String urlStr){ HttpDownloader httpDownloader=new HttpDownloader();String result=httpDownloader.download(urlStr); return result; } private List<Mp3Info> parse(String xmlStr){ SAXParserFactory saxParserFactory=SAXParserFactory.newInstance();List<Mp3Info> infos=new ArrayList<Mp3Info>(); try {//SAX解析XML檔案固定寫法 XMLReader xmlReader=saxParserFactory.newSAXParser().getXMLReader();//為XMLReader設定內容處理器 Mp3ListContentHandler mp3ListContentHandler=new Mp3ListContentHandler(infos);xmlReader.setContentHandler(mp3ListContentHandler);//開始解析檔案xmlReader.parse(new InputSource(new StringReader(xmlStr)));for (Iterator iterator = infos.iterator(); iterator.hasNext();) {Mp3Info mp3Info = (Mp3Info) iterator.next();System.out.println(mp3Info);} } catch (Exception e) {e.printStackTrace();} return null; }}