Android实例RSS客户端开发(2)--解析XML文件
? 一
介紹完RSS之后,下面開始講解如何解析RSS文件。因?yàn)镽SS是基于XML的,所以我們就直接介紹如何解析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)聽器)來(lái)工作的。一個(gè)可以產(chǎn)生事件的對(duì)象叫做事件源,而一個(gè)可以針對(duì)事件做出響應(yīng)的對(duì)象就被叫做事件處理器。
在SAX接口中,事件源是org.xml.sax包中的XMLReader,他通過(guò)parse()方法開始解析XML文檔,并根據(jù)文檔內(nèi)容產(chǎn)生事件。而事件處理器則是org.xml.sax包中的ContentHandler、DTDHandler、ErrorHandler,以及EntityResolver這四個(gè)接口。他們分別處理事件源在解析過(guò)程中產(chǎn)生不同類的事件(其中DTDHandler為解析文檔DTD時(shí)所用)。
SAX是一種占用內(nèi)存少且解析速度快的解析器,它采用的是事件啟動(dòng),它不需要解析完整個(gè)文檔,而是按照內(nèi)容順序 看文檔某個(gè)部分是否符合xml語(yǔ)法,如果符合就觸發(fā)相應(yīng)的事件,所謂的事件就是些回調(diào)方法(callback),這些方法 定義在ContentHandler中,下面是其主要方法:startDocument:當(dāng)遇到文檔的時(shí)候就觸發(fā)這個(gè)事件 調(diào)用這個(gè)方法 可以在其中做些預(yù)處理工作
startElement: (String namespaceURI,String localName,String qName,Attributes atts)當(dāng)遇開始標(biāo)簽的時(shí)候就會(huì)觸發(fā)這個(gè)方法。
endElement(String uri,String localName,String name):當(dāng)遇到結(jié)束標(biāo)簽時(shí)觸發(fā)這個(gè)事件,調(diào)用此法可以做些善后工作。
charachers(char [] ch,int start,int length):當(dāng)遇到xml內(nèi)容時(shí)觸發(fā)這個(gè)方法,用new String(ch,start,length)可以接受內(nèi)容。?
?二?建立pojo類?
在解析之前,我們需要建立pojo類來(lái)對(duì)應(yīng)RSS中的元素。首先是RSS feed,我們知道<channel> 元素用于描述 RSS feed,但它不是描述RSS的重點(diǎn),它下面的三個(gè)必須子元素<title><link><description>是描述feed的主要信息。因?yàn)槲覀冊(cè)诮馕鲋熬?/span>
事先獲取了RSS地址,所以在這里我們就不需要建立一個(gè)RSS的link了。主要建立link,item列表以及description,因?yàn)槭菢?biāo)題,所以把description就換成時(shí)間來(lái)表達(dá),一般的RSS也是這樣做的。如圖:
下面是建立的RSSFeed:
public class RSSFeed?
{
private String title = null;標(biāo)題
private String pubdate = null;發(fā)布日期
private int itemcount = 0;//用于計(jì)算列表數(shù)目
private List<RSSItem> itemlist;聲明一個(gè)RSSItem類型的泛型集合類List對(duì)象itemlist,用于描述列表?item
?public RSSFeed()
{
itemlist = new Vector(0); 構(gòu)造函數(shù)初始化itemlist
}
public int addItem(RSSItem item)
{
itemlist.add(item);
itemcount++;
return itemcount;
}
public RSSItem getItem(int location)
{
return itemlist.get(location);
}
public List getAllItems()
{
return itemlist;
}
public List getAllItemsForListView(){
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;
}
int getItemCount()
{
return itemcount;
}
public void setTitle(String title)
{
this.title = title;
}
public void setPubDate(String pubdate)
{
this.pubdate = pubdate;
}
public String getTitle()
{
return title;
}
public String getPubDate()
{
return pubdate;
}
}
建立完RSSFeed 類后,我們開始建立?RSSItem
public static final String TITLE="title";
public static final String PUBDATE="pubdate";
private String title = null;
private String description = null;
private String link = null;
private String category = null;
private String pubdate = null;
? ? ? ?
? ? ? ? public RSSItem()
{
}
public void setTitle(String title)
{
this.title = title;
}
public void setDescription(String description)
{
this.description = description;
}
public void setLink(String link)
{
this.link = link;
}
public void setCategory(String category)
{
this.category = category;
}
public void setPubDate(String pubdate)
{
this.pubdate = pubdate;
}
public String getTitle()
{
return title;
}
public String getDescription()
{
return description;
}
public String getLink()
{
return link;
}
public String getCategory()
{
return category;
}
public String getPubDate()
{
return pubdate;
}
public String toString()
{
if (title.length() > 20)
{
return title.substring(0, 42) + "...";
}
return title;
}
}
三 解析
新建helper類RSSHandler,用于對(duì)rss進(jìn)行xml解析,并將解析結(jié)果包裝為RSSFeed和RSSItem對(duì)象,方便在ui界面中顯示:
public class RSSHandler extends DefaultHandler?
{
RSSFeed rssFeed;//用于保存解析過(guò)程中的channel
RSSItem rssItem;//用于保存解析過(guò)程中的item
String lastElementName = "";?//標(biāo)記變量,用于標(biāo)記在解析過(guò)程中我們關(guān)心的幾個(gè)標(biāo)簽,若不是我們關(guān)心的標(biāo)簽,記做?0
final int RSS_TITLE = 1;//若是title標(biāo)簽,記做?1,注意有兩個(gè)title,但我們都保存在item的title成員變量中
final int RSS_LINK = 2;//若是link標(biāo)簽,記做?2
final int RSS_DESCRIPTION = 3;//若是description標(biāo)簽,記做?3
final int RSS_CATEGORY = 4;//若是category標(biāo)簽,記做?4
final int RSS_PUBDATE = 5;?//若是pubdate標(biāo)簽,記做?5,注意有兩個(gè)pubdate,但我們都保存在item的pubdate成員變量中
int currentstate = 0;
? ? ? ? public RSSHandler(){}
public RSSFeed getFeed()
{
return rssFeed;//通過(guò)這個(gè)方法把解析結(jié)果封裝在?RSSFeed?對(duì)象中并返回
}
?//下面通過(guò)重載?DefaultHandler?的?5?個(gè)方法來(lái)實(shí)現(xiàn)?sax?解析
public void startDocument() throws SAXException
{
? ? ? ? ? ? ? ? //這個(gè)方法在解析xml文檔的一開始執(zhí)行,一般我們需要在該方法中初始化解析過(guò)程中有可能用到的變量
rssFeed = new RSSFeed();
rssItem = new RSSItem();
? ? ? ? }
public void endDocument() throws SAXException
{
//這個(gè)方法在整個(gè)xml文檔解析結(jié)束時(shí)執(zhí)行,一般需要在該方法中返回或保存整個(gè)文檔解析解析結(jié)果,但由于
?????//我們已經(jīng)在解析過(guò)程中把結(jié)果保持在rssFeed中,所以這里什么也不做
}
public void startElement(String namespaceURI, String localName,String qName, Attributes atts) throws SAXException
{
//這個(gè)方法在解析標(biāo)簽開始標(biāo)記時(shí)執(zhí)行,一般我們需要在該方法取得標(biāo)簽屬性值,但由于我們的rss文檔
?????//中并沒(méi)有任何我們關(guān)心的標(biāo)簽屬性,因此我們主要在這里進(jìn)行的是設(shè)置標(biāo)記變量currentstate,以
?????//標(biāo)記我們處理到哪個(gè)標(biāo)簽
if (localName.equals("channel"))
{//channel這個(gè)標(biāo)簽沒(méi)有任何值得我們關(guān)心的內(nèi)容,所以currentstate置為0
currentstate = 0;
return;
}
if (localName.equals("item"))
{
//若是item標(biāo)簽,則重新構(gòu)造一個(gè)RSSItem,從而把已有(已經(jīng)解析過(guò)的)item數(shù)據(jù)扔掉,當(dāng)
?//然事先是已經(jīng)保存到rssFeed的itemlist集合中了
rssItem = new RSSItem();
return;
}
if (localName.equals("title"))
{
//若是title標(biāo)簽,置currentstate為1,表明這是我們關(guān)心的數(shù)據(jù),這樣在characters
?//方法中會(huì)把元素內(nèi)容保存到rssItem變量中
currentstate = RSS_TITLE;
return;
}
if (localName.equals("description"))
{
//若是description標(biāo)簽,置currentstate為3,表明這是我們關(guān)心的數(shù)據(jù),這樣在characters
?//方法中會(huì)把元素內(nèi)容保存到rssItem變量中
currentstate = RSS_DESCRIPTION;
return;
}
if (localName.equals("link"))
{
//若是link標(biāo)簽,置currentstate為2,表明這是我們關(guān)心的數(shù)據(jù),這樣在characters
?//方法中會(huì)把元素內(nèi)容保存到rssItem變量中
currentstate = RSS_LINK;
return;
}
if (localName.equals("category"))
{
//若是category標(biāo)簽,置currentstate為4,表明這是我們關(guān)心的數(shù)據(jù),這樣在characters
?//方法中會(huì)把元素內(nèi)容保存到rssItem變量中
currentstate = RSS_CATEGORY;
return;
}
if (localName.equals("pubDate"))
{
//若是pubDate標(biāo)簽,置currentstate為5,表明這是我們關(guān)心的數(shù)據(jù),這樣在characters
?//方法中會(huì)把元素內(nèi)容保存到rssItem變量中
currentstate = RSS_PUBDATE;
return;
}
currentstate = 0;//如果不是上面列出的任何標(biāo)簽,置currentstate為0,我們不關(guān)心
}
public void endElement(String namespaceURI, String localName, String qName) throws SAXException
{
//如果解析一個(gè)item節(jié)點(diǎn)結(jié)束,就將rssItem添加到rssFeed中。
if (localName.equals("item"))
{
rssFeed.addItem(rssItem);
return;
}
}
public void characters(char ch[], int start, int length)
{//這個(gè)方法在解析標(biāo)簽內(nèi)容(即開始標(biāo)記-結(jié)束標(biāo)記之間的部分)時(shí)執(zhí)行,一般我們?cè)诶镞@獲取元素體內(nèi)容
String theString = new String(ch,start,length);?//獲取元素體內(nèi)容
switch (currentstate)
{//根據(jù)currentstate標(biāo)記判斷這個(gè)元素體是屬于我們關(guān)心的哪個(gè)元素
case RSS_TITLE:
rssItem.setTitle(theString);//若是title元素,放入rssItem的title屬性
currentstate = 0;
break;
case RSS_LINK:
rssItem.setLink(theString);//若是link元素,放入rssItem的link屬性
currentstate = 0;
break;
case RSS_DESCRIPTION:
rssItem.setDescription(theString);
currentstate = 0;
break;
case RSS_CATEGORY:
rssItem.setCategory(theString);
currentstate = 0;
break;
case RSS_PUBDATE:
rssItem.setPubDate(theString);
currentstate = 0;
break;
default:
return;
}
}
}
之后就可以按照一定的步驟來(lái)進(jìn)行解析了,具體如下:
private RSSFeed getFeed(String urlString)
? ? {
? ? try
? ? {
? ? ? URL url = new URL(urlString);
? ? ? ? ? SAXParserFactory factory = SAXParserFactory.newInstance();??//?構(gòu)建Sax解析工廠
? ? ? ? ? ?SAXParser parser = factory.newSAXParser();?//?使用Sax解析工廠構(gòu)建Sax解析器
? ? ? ? ? ?XMLReader xmlreader = parser.getXMLReader();???//?使用Sax解析器構(gòu)建xml?Reader
? ? ? ? ? ?
? ? ? ? ? ?RSSHandler rssHandler = new RSSHandler();?//?構(gòu)建自定義的RSSHandler作為xml?Reader的處理器(或代理)
? ? ? ? ? ?xmlreader.setContentHandler(rssHandler);?? ??//?構(gòu)建自定義的RSSHandler作為xml?Reader的處理器(或代理)
? ? ? ? ? ?InputSource is = new InputSource(url.openStream()); ? ???//?使用url打開流,并將流作為xml?Reader的輸入源并解析
? ? ? ? ? ?xmlreader.parse(is);
??
? ? ? ? ? ?return rssHandler.getFeed();? ???//?將解析結(jié)果作為?RSSFeed?對(duì)象返回
? ? }
? ? catch (Exception ee)
? ? {
? ? return null;
? ? }
? ? }
RSS文件的解析就介紹到這里,之后我們開始實(shí)現(xiàn)如何在列表里顯示RSS內(nèi)容了。
轉(zhuǎn)載于:https://www.cnblogs.com/android-html5/archive/2012/06/01/2533929.html
總結(jié)
以上是生活随笔為你收集整理的Android实例RSS客户端开发(2)--解析XML文件的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: QString 与中文问题
- 下一篇: Android开机自启动 .