詳解android使用SAX解析XML文件
解析XML的方式有很多種,大家比較熟悉的可能就是DOM解析。
DOM(文件對(duì)象模型)解析:解析器讀入整個(gè)文檔,然后構(gòu)建一個(gè)駐留內(nèi)存的樹結(jié)構(gòu),然后代碼就可以根據(jù)DOM接口來(lái)操作這個(gè)樹結(jié)構(gòu)了。
優(yōu)點(diǎn):整個(gè)文檔讀入內(nèi)存,方便操作:支持修改、刪除和重現(xiàn)排列等多種功能。
缺點(diǎn):將整個(gè)文檔讀入內(nèi)存中,保留了過(guò)多的不需要的節(jié)點(diǎn),浪費(fèi)內(nèi)存和空間。
使用場(chǎng)合:一旦讀入文檔,還需要多次對(duì)文檔進(jìn)行操作,并且在硬件資源充足的情況下(內(nèi)存,CPU)。
為了解決DOM解析存在的問(wèn)題,就出現(xiàn)了SAX解析。其特點(diǎn)為:
優(yōu)點(diǎn):不用實(shí)現(xiàn)調(diào)入整個(gè)文檔,占用資源少。尤其在嵌入式環(huán)境中,如android,極力推薦使用SAX解析。
缺點(diǎn):不像DOM解析一樣將文檔長(zhǎng)期駐留在內(nèi)存中,數(shù)據(jù)不是持久的。如果事件過(guò)后沒(méi)有保存數(shù)據(jù),數(shù)據(jù)就會(huì)丟失。
使用場(chǎng)合:機(jī)器有性能限制。
SAX解析XML文檔采用事件驅(qū)動(dòng)模式。什么是事件驅(qū)動(dòng)模式?它將XML文檔轉(zhuǎn)換成一系列的事件,由單獨(dú)的事件處理器來(lái)決定如何處理。
基于事件驅(qū)動(dòng)的處理模式主要是基于事件源和事件處理器(或者叫監(jiān)聽(tīng)器)來(lái)工作的。一個(gè)可以產(chǎn)生事件的對(duì)象叫做事件源,而一個(gè)可以針對(duì)事件做出響應(yīng)的對(duì)象就被叫做事件處理器。
在SAX接口中,事件源是org.xml.sax包中的XMLReader,他通過(guò)parse()方法開(kāi)始解析XML文檔,并根據(jù)文檔內(nèi)容產(chǎn)生事件。而事件處理器則是org.xml.sax包中的ContentHandler、DTDHandler、ErrorHandler,以及EntityResolver這四個(gè)接口。他們分別處理事件源在解析過(guò)程中產(chǎn)生不同類的事件(其中DTDHandler為解析文檔DTD時(shí)所用)。詳細(xì)介紹如下表:
在上述四個(gè)接口中,最重要的就是ContentHandler這個(gè)接口,下面是對(duì)這個(gè)接口方法的說(shuō)明:
//設(shè)置一個(gè)可以定位文檔內(nèi)容事件發(fā)生位置的定位器對(duì)象 public void setDocumentLocator(Locator locator) //用于處理文檔解析開(kāi)始事件 public void startDocument()throws SAXException //處理元素開(kāi)始事件,從參數(shù)中可以獲得元素所在名稱空間的uri,元素名稱,屬性類表等信息 public void startElement(String namespacesURI , String localName , String qName , Attributes atts) throws SAXException //處理元素結(jié)束事件,從參數(shù)中可以獲得元素所在名稱空間的uri,元素名稱等信息 public void endElement(String namespacesURI , String localName , String qName) throws SAXException //處理元素的字符內(nèi)容,從參數(shù)中可以獲得內(nèi)容 public void characters(char[] ch , int start , int length) throws SAXException
這里再介紹下XMLReader中的方法。
//注冊(cè)處理XML文檔解析事件ContentHandler public void setContentHandler(ContentHandler handler) //開(kāi)始解析一個(gè)XML文檔 public void parse(InputSorce input) throws SAXException
SAX實(shí)現(xiàn)實(shí)體解析的步驟:
在android中使用SAX是有跡可循的,完全可以按照下面的方法就可以輕松找到xml里的tag,然后得到想要的內(nèi)容。具體實(shí)現(xiàn)步驟如下:
(一)第一步:新建一個(gè)工廠類SAXParserFactory,代碼如下:
SAXParserFactory factory = SAXParserFactory.newInstance();
(二)第二步:讓工廠類產(chǎn)生一個(gè)SAX的解析類SAXParser,代碼如下:
SAXParser parser = factory.newSAXParser();
(三)第三步:從SAXPsrser中得到一個(gè)XMLReader實(shí)例,代碼如下:
XMLReader reader = parser.getXMLReader();
(四)第四步:把自己寫的handler注冊(cè)到XMLReader中,一般最重要的就是ContentHandler,代碼如下:
RSSHandler handler = new RSSHandler(); reader.setContentHandler(handler);
(五)第五步:將一個(gè)xml文檔或者資源變成一個(gè)java可以處理的InputStream流后,解析正式開(kāi)始,代碼如下:
parser.parse(is);
上面幾個(gè)步驟中,最重要、最關(guān)鍵的就是第四步,handler的實(shí)現(xiàn)。
下面通過(guò)一個(gè)RSS解析的例子說(shuō)明handler的實(shí)現(xiàn):
我們先是自己見(jiàn)一個(gè)rss的xml文檔,實(shí)現(xiàn)本地解析,新建的rss文檔如下:
<?xml version="1.0" encoding="UTF-8"?> <channel> <title>RSS 解析練習(xí)</title> <description>hehehaha</description> <link>http://www.cnblogs.com/felix-hua/</link> <language>zh-cn</language> <item> <title><![CDATA[頭條]]></title> <link>http://mc.cz001.com.cn/images/menu/23_active.png</link> <category>0</category> <description>描述詳細(xì)信息的</description> <pubDate>2012-01-09</pubDate> </item> <item> <title><![CDATA[新聞]]></title> <link>http://mc.cz001.com.cn/images/menu/23_active.png</link> <category>0</category> <description>描述詳細(xì)信息的</description> <pubDate>2012-01-09</pubDate> </item> <item> <title><![CDATA[首頁(yè)]]></title> <link>http://mc.cz001.com.cn/images/menu/23_active.png</link> <category>0</category> <description>描述詳細(xì)信息的</description> <pubDate>2012-01-09</pubDate> </item> <item> <title><![CDATA[財(cái)經(jīng)]]></title> <link>http://mc.cz001.com.cn/images/menu/23_active.png</link> <category>0</category> <description>描述詳細(xì)信息的</description> <pubDate>2012-01-09</pubDate> </item>
建好后,我們命名為rssxml.xml,然后放到項(xiàng)目的根目錄下:
然后我們可以建立兩個(gè)實(shí)體類:
1、RSSFeed,與完整的xml文檔相對(duì)應(yīng);
2、RSSItem,與item標(biāo)簽內(nèi)的信息相對(duì)應(yīng)。
這樣在解析xml時(shí),我們就可以把解析出來(lái)的信息放到實(shí)體類里,然后直接操作實(shí)體類就可以了。下面給出代碼:
RSSFeed.java
package com.sax.org.entity; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Vector; public class RSSFeed { private String title; private int itemcount; private List<RSSItem> itemlist; public RSSFeed(){ itemlist = new Vector<RSSItem>(0); } /** * 負(fù)責(zé)將一個(gè)RSSItem加入到RSSFeed類中 * @param item * @return */ public int addItem(RSSItem item){ itemlist.add(item); itemcount++; return itemcount; } public RSSItem getItem(int location){ return itemlist.get(location); } public List<RSSItem> getAllItems(){ return itemlist; } /** * 負(fù)責(zé)從RSSFeed類中生成列表所需要的數(shù)據(jù) * @return */ public List getAllItemForListView(){ List<Map<String, Object>> data = new ArrayList<Map<String,Object>>(); int size = itemlist.size(); for(int i=0 ; i<size ; i++){ HashMap<String , Object> item = new HashMap<String, Object>(); item.put(RSSItem.TITLE, itemlist.get(i).getTitle()); item.put(RSSItem.PUBDATE, itemlist.get(i).getPubdate()); data.add(item); } return data; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public int getItemcount() { return itemcount; } public void setItemcount(int itemcount) { this.itemcount = itemcount; } public List<RSSItem> getItemlist() { return itemlist; } public void setItemlist(List<RSSItem> itemlist) { this.itemlist = itemlist; } }
RSSItem.java
package com.sax.org.entity; public class RSSItem { public static String TITLE = "title"; public static String PUBDATE = "pubdate"; public String title; public String description; public String link; public String category; public String pubdate; public RSSItem() { } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getLink() { return link; } public void setLink(String link) { this.link = link; } public String getCategory() { return category; } public void setCategory(String category) { this.category = category; } public String getPubdate() { return pubdate; } public void setPubdate(String pubdate) { this.pubdate = pubdate; } }
下面就是最最重要的地方了,建立自己的ContentHandler.看下面的代碼:
RSSHandler.java
package com.sax.org.handler; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import com.sax.org.entity.RSSFeed; import com.sax.org.entity.RSSItem; public class RSSHandler extends DefaultHandler{ RSSFeed RssFeed; RSSItem RssItem; final int RSS_TITLE = 1; final int RSS_LINK = 2; final int RSS_DESCRIPTION = 3; final int RSS_CATEGORY = 4; final int RSS_PUBDATE = 5; int currentstate = 0; public RSSHandler(){} public RSSFeed getFeed(){ return RssFeed; } @Override public void startDocument() throws SAXException { // TODO Auto-generated method stub RssFeed = new RSSFeed(); RssItem = new RSSItem(); } @Override public void endDocument() throws SAXException { // TODO Auto-generated method stub } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { // TODO Auto-generated method stub if(localName.equals("channel")){ currentstate = 0; return; } if(localName.equals("item")){ RssItem = new RSSItem(); return; } if(localName.equals("title")){ currentstate = RSS_TITLE; return; } if(localName.equals("description")){ currentstate = RSS_DESCRIPTION; return; } if(localName.equals("link")){ currentstate = RSS_LINK; return; } if(localName.equals("category")){ currentstate = RSS_CATEGORY; return; } if(localName.equals("pubDate")){ currentstate = RSS_PUBDATE; return; } currentstate = 0; } @Override public void endElement(String uri, String localName, String qName) throws SAXException { // TODO Auto-generated method stub if(localName.equals("item")){ RssFeed.addItem(RssItem); return; } } @Override public void characters(char[] ch, int start, int length) throws SAXException { // TODO Auto-generated method stub String theString = new String(ch, start, length); switch(currentstate){ case RSS_TITLE: RssItem.setTitle(theString); currentstate = 0; break; case RSS_DESCRIPTION: RssItem.setDescription(theString); currentstate = 0; break; case RSS_LINK: RssItem.setLink(theString); currentstate = 0; break; case RSS_PUBDATE: RssItem.setPubdate(theString); currentstate = 0; break; case RSS_CATEGORY: RssItem.setCategory(theString); currentstate = 0; break; default: return; } } }
就上面的代碼分析,實(shí)現(xiàn)一個(gè)ContentHandler一般要一下幾個(gè)步驟:
1、聲明一個(gè)類,繼承DefaultHandler。DefaultHandler是一個(gè)基類,這個(gè)類里面簡(jiǎn)單實(shí)現(xiàn)了一個(gè)ContentHandler。我們只需要重寫里面的方法即可。
2、重寫 startDocument() 和 endDocument(),一般解析將正式解析之前的一些初始化工資放到startDocument()里面,收尾的工作放到endDocument()里面。
3、重寫startElement(),XML解析器遇到XML里面的tag時(shí)就會(huì)調(diào)用這個(gè)函數(shù)。經(jīng)常在這個(gè)函數(shù)內(nèi)是通過(guò)localName倆進(jìn)行判斷而操作一些數(shù)據(jù)。
4、重寫characters()方法,這是一個(gè)回調(diào)方法。解析器執(zhí)行完startElement()后,解析完節(jié)點(diǎn)的內(nèi)容后就會(huì)執(zhí)行這個(gè)方法,并且參數(shù)ch[]就是節(jié)點(diǎn)的內(nèi)容。這個(gè)例子里我們根據(jù)currentstate的不同,來(lái)判斷當(dāng)前那個(gè)tag的內(nèi)容,并放到合適的實(shí)體類中。
5、重寫endElement()方法,這個(gè)方法與startElement()相對(duì)應(yīng),解析完一個(gè)tag節(jié)點(diǎn)后,執(zhí)行這個(gè)方法。再找個(gè)例子中,如果解析一個(gè)item結(jié)束,就將RSSIiem添加到RSSFeed中。
最后我們實(shí)現(xiàn)一個(gè)activity來(lái)展現(xiàn)解析的結(jié)果:
package com.sax.org; import java.io.IOException; import java.net.URL; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import android.app.Activity; import android.os.Bundle; import android.widget.ListView; import android.widget.SimpleAdapter; import android.widget.TextView; import com.sax.org.entity.RSSFeed; import com.sax.org.entity.RSSItem; import com.sax.org.handler.RSSHandler; public class SAXReaderActivity extends Activity { /** Called when the activity is first created. */ public String rssUrl = "http://mc.cz001.com.cn/a/indexconfig/index.rss"; public RSSFeed feed; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); feed = getFeed(rssUrl); showList(); } public RSSFeed getFeed(String rssUrl) { try { // 這里我們實(shí)現(xiàn)了本地解析,所以注掉了這個(gè)取網(wǎng)絡(luò)數(shù)據(jù)的。 // URL url = new URL(rssUrl); SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParser parser = factory.newSAXParser(); XMLReader reader = parser.getXMLReader(); RSSHandler handler = new RSSHandler(); reader.setContentHandler(handler); InputSource is = new InputSource(this.getClassLoader().getResourceAsStream("rssxml.xml"));//取得本地xml文件 reader.parse(is); return handler.getFeed(); } catch (ParserConfigurationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SAXException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } public void showList() { ListView rsslistview = (ListView) findViewById(R.id.rssList); TextView rsstitle = (TextView) findViewById(R.id.rsstitle); if (feed == null) { rsstitle.setText("訪問(wèn)失敗..."); return; } SimpleAdapter adapter = new SimpleAdapter(this, feed.getAllItemForListView(), android.R.layout.simple_list_item_2, new String[] { RSSItem.TITLE, RSSItem.PUBDATE }, new int[] { android.R.id.text1, android.R.id.text2 }); rsslistview.setAdapter(adapter); } }
展示下運(yùn)行結(jié)果:
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android雷達(dá)掃描動(dòng)態(tài)界面制作
這篇文章主要為大家詳細(xì)介紹了Android雷達(dá)掃描動(dòng)態(tài)界面制作資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-10-10Android手機(jī)衛(wèi)士之確認(rèn)密碼對(duì)話框
這篇文章主要為大家詳細(xì)介紹了Android手機(jī)衛(wèi)士之確認(rèn)密碼對(duì)話框,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-10-1025個(gè)實(shí)用酷炫的Android開(kāi)源UI框架
本文為大家分享了25個(gè)實(shí)用酷炫的Android開(kāi)源UI框架,靈活運(yùn)用這些UI框架可在日常工作中節(jié)省不少時(shí)間2018-04-04實(shí)例講解Android應(yīng)用中自定義組合控件的方法
這篇文章主要介紹了實(shí)例講解Android應(yīng)用中自定義組合控件的方法,通過(guò)例子講解了view組合控件及自定義屬性的用法,需要的朋友可以參考下2016-04-04Android獲取arrays.xml里的數(shù)組字段值實(shí)例詳解
這篇文章主要介紹了Android獲取arrays.xml里的數(shù)組字段值實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-04-04Android中的廣播、服務(wù)、數(shù)據(jù)庫(kù)、通知、包等術(shù)語(yǔ)的原理和介紹(圖解)
這篇文章主要介紹了Android中的廣播、服務(wù)、數(shù)據(jù)庫(kù)、通知、包等術(shù)語(yǔ)的原理和介紹(圖解),本文用圖文方式總結(jié)了Android中的一些開(kāi)發(fā)術(shù)語(yǔ),需要的朋友可以參考下2014-10-10