天天看点

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()

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 setDescription(String description)

this.description = description;

void setLink(String link)

this.link = link;

void setCategory(String category)

this.category = category;

void setPubDate(String pubdate)

String getDescription()

return description;

String getLink()

return link;

public String getCategory()

return category;

public String toString()

if (title.length() > 20)

return title.substring(0, 42) + "...";

三 解析

新建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集合中了

if (localName.equals("title"))

//若是title标签,置currentstate为1,表明这是我们关心的数据,这样在characters

 //方法中会把元素内容保存到rssItem变量中

currentstate = RSS_TITLE;

if (localName.equals("description"))

//若是description标签,置currentstate为3,表明这是我们关心的数据,这样在characters

currentstate = RSS_DESCRIPTION;

if (localName.equals("link"))

//若是link标签,置currentstate为2,表明这是我们关心的数据,这样在characters

currentstate = RSS_LINK;

if (localName.equals("category"))

//若是category标签,置currentstate为4,表明这是我们关心的数据,这样在characters

currentstate = RSS_CATEGORY;

if (localName.equals("pubDate"))

//若是pubDate标签,置currentstate为5,表明这是我们关心的数据,这样在characters

currentstate = RSS_PUBDATE;

currentstate = 0;//如果不是上面列出的任何标签,置currentstate为0,我们不关心

public void endElement(String namespaceURI, String localName, String qName) throws SAXException

//如果解析一个item节点结束,就将rssItem添加到rssFeed中。

rssFeed.addItem(rssItem);

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属性

break;

case RSS_LINK:

rssItem.setLink(theString);//若是link元素,放入rssItem的link属性

case RSS_DESCRIPTION:

rssItem.setDescription(theString);

case RSS_CATEGORY:

rssItem.setCategory(theString);

case RSS_PUBDATE:

rssItem.setPubDate(theString);

default: