Android实例RSS客户端开发(2)--解析XML文件
? 一
介紹完RSS之后,下面開始講解如何解析RSS文件。因為RSS是基于XML的,所以我們就直接介紹如何解析XML文件。
解析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時所用)。
SAX是一種占用內存少且解析速度快的解析器,它采用的是事件啟動,它不需要解析完整個文檔,而是按照內容順序 看文檔某個部分是否符合xml語法,如果符合就觸發相應的事件,所謂的事件就是些回調方法(callback),這些方法 定義在ContentHandler中,下面是其主要方法:startDocument:當遇到文檔的時候就觸發這個事件 調用這個方法 可以在其中做些預處理工作
startElement: (String namespaceURI,String localName,String qName,Attributes atts)當遇開始標簽的時候就會觸發這個方法。
endElement(String uri,String localName,String name):當遇到結束標簽時觸發這個事件,調用此法可以做些善后工作。
charachers(char [] ch,int start,int length):當遇到xml內容時觸發這個方法,用new String(ch,start,length)可以接受內容。?
?二?建立pojo類?
在解析之前,我們需要建立pojo類來對應RSS中的元素。首先是RSS feed,我們知道<channel> 元素用于描述 RSS feed,但它不是描述RSS的重點,它下面的三個必須子元素<title><link><description>是描述feed的主要信息。因為我們在解析之前就
事先獲取了RSS地址,所以在這里我們就不需要建立一個RSS的link了。主要建立link,item列表以及description,因為是標題,所以把description就換成時間來表達,一般的RSS也是這樣做的。如圖:
下面是建立的RSSFeed:
public class RSSFeed?
{
private String title = null;標題
private String pubdate = null;發布日期
private int itemcount = 0;//用于計算列表數目
private List<RSSItem> itemlist;聲明一個RSSItem類型的泛型集合類List對象itemlist,用于描述列表?item
?public RSSFeed()
{
itemlist = new Vector(0); 構造函數初始化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,用于對rss進行xml解析,并將解析結果包裝為RSSFeed和RSSItem對象,方便在ui界面中顯示:
public class RSSHandler extends DefaultHandler?
{
RSSFeed rssFeed;//用于保存解析過程中的channel
RSSItem rssItem;//用于保存解析過程中的item
String lastElementName = "";?//標記變量,用于標記在解析過程中我們關心的幾個標簽,若不是我們關心的標簽,記做?0
final int RSS_TITLE = 1;//若是title標簽,記做?1,注意有兩個title,但我們都保存在item的title成員變量中
final int RSS_LINK = 2;//若是link標簽,記做?2
final int RSS_DESCRIPTION = 3;//若是description標簽,記做?3
final int RSS_CATEGORY = 4;//若是category標簽,記做?4
final int RSS_PUBDATE = 5;?//若是pubdate標簽,記做?5,注意有兩個pubdate,但我們都保存在item的pubdate成員變量中
int currentstate = 0;
? ? ? ? public RSSHandler(){}
public RSSFeed getFeed()
{
return rssFeed;//通過這個方法把解析結果封裝在?RSSFeed?對象中并返回
}
?//下面通過重載?DefaultHandler?的?5?個方法來實現?sax?解析
public void startDocument() throws SAXException
{
? ? ? ? ? ? ? ? //這個方法在解析xml文檔的一開始執行,一般我們需要在該方法中初始化解析過程中有可能用到的變量
rssFeed = new RSSFeed();
rssItem = new RSSItem();
? ? ? ? }
public void endDocument() throws SAXException
{
//這個方法在整個xml文檔解析結束時執行,一般需要在該方法中返回或保存整個文檔解析解析結果,但由于
?????//我們已經在解析過程中把結果保持在rssFeed中,所以這里什么也不做
}
public void startElement(String namespaceURI, String localName,String qName, Attributes atts) throws SAXException
{
//這個方法在解析標簽開始標記時執行,一般我們需要在該方法取得標簽屬性值,但由于我們的rss文檔
?????//中并沒有任何我們關心的標簽屬性,因此我們主要在這里進行的是設置標記變量currentstate,以
?????//標記我們處理到哪個標簽
if (localName.equals("channel"))
{//channel這個標簽沒有任何值得我們關心的內容,所以currentstate置為0
currentstate = 0;
return;
}
if (localName.equals("item"))
{
//若是item標簽,則重新構造一個RSSItem,從而把已有(已經解析過的)item數據扔掉,當
?//然事先是已經保存到rssFeed的itemlist集合中了
rssItem = new RSSItem();
return;
}
if (localName.equals("title"))
{
//若是title標簽,置currentstate為1,表明這是我們關心的數據,這樣在characters
?//方法中會把元素內容保存到rssItem變量中
currentstate = RSS_TITLE;
return;
}
if (localName.equals("description"))
{
//若是description標簽,置currentstate為3,表明這是我們關心的數據,這樣在characters
?//方法中會把元素內容保存到rssItem變量中
currentstate = RSS_DESCRIPTION;
return;
}
if (localName.equals("link"))
{
//若是link標簽,置currentstate為2,表明這是我們關心的數據,這樣在characters
?//方法中會把元素內容保存到rssItem變量中
currentstate = RSS_LINK;
return;
}
if (localName.equals("category"))
{
//若是category標簽,置currentstate為4,表明這是我們關心的數據,這樣在characters
?//方法中會把元素內容保存到rssItem變量中
currentstate = RSS_CATEGORY;
return;
}
if (localName.equals("pubDate"))
{
//若是pubDate標簽,置currentstate為5,表明這是我們關心的數據,這樣在characters
?//方法中會把元素內容保存到rssItem變量中
currentstate = RSS_PUBDATE;
return;
}
currentstate = 0;//如果不是上面列出的任何標簽,置currentstate為0,我們不關心
}
public void endElement(String namespaceURI, String localName, String qName) throws SAXException
{
//如果解析一個item節點結束,就將rssItem添加到rssFeed中。
if (localName.equals("item"))
{
rssFeed.addItem(rssItem);
return;
}
}
public void characters(char ch[], int start, int length)
{//這個方法在解析標簽內容(即開始標記-結束標記之間的部分)時執行,一般我們在里這獲取元素體內容
String theString = new String(ch,start,length);?//獲取元素體內容
switch (currentstate)
{//根據currentstate標記判斷這個元素體是屬于我們關心的哪個元素
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;
}
}
}
之后就可以按照一定的步驟來進行解析了,具體如下:
private RSSFeed getFeed(String urlString)
? ? {
? ? try
? ? {
? ? ? URL url = new URL(urlString);
? ? ? ? ? SAXParserFactory factory = SAXParserFactory.newInstance();??//?構建Sax解析工廠
? ? ? ? ? ?SAXParser parser = factory.newSAXParser();?//?使用Sax解析工廠構建Sax解析器
? ? ? ? ? ?XMLReader xmlreader = parser.getXMLReader();???//?使用Sax解析器構建xml?Reader
? ? ? ? ? ?
? ? ? ? ? ?RSSHandler rssHandler = new RSSHandler();?//?構建自定義的RSSHandler作為xml?Reader的處理器(或代理)
? ? ? ? ? ?xmlreader.setContentHandler(rssHandler);?? ??//?構建自定義的RSSHandler作為xml?Reader的處理器(或代理)
? ? ? ? ? ?InputSource is = new InputSource(url.openStream()); ? ???//?使用url打開流,并將流作為xml?Reader的輸入源并解析
? ? ? ? ? ?xmlreader.parse(is);
??
? ? ? ? ? ?return rssHandler.getFeed();? ???//?將解析結果作為?RSSFeed?對象返回
? ? }
? ? catch (Exception ee)
? ? {
? ? return null;
? ? }
? ? }
RSS文件的解析就介紹到這里,之后我們開始實現如何在列表里顯示RSS內容了。
轉載于:https://www.cnblogs.com/android-html5/archive/2012/06/01/2533929.html
總結
以上是生活随笔為你收集整理的Android实例RSS客户端开发(2)--解析XML文件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: QString 与中文问题
- 下一篇: 给我一个兴趣点,我就能撬动一个行业