android SAX解析XML

解析XML的方式有不少种,你们比较熟悉的可能就是DOM解析。 java

DOM(文件对象模型)解析:解析器读入整个文档,而后构建一个驻留内存的树结构,而后代码就能够根据DOM接口来操做这个树结构了。 android

优势:整个文档读入内存,方便操做:支持修改、删除和重现排列等多种功能。 ide

缺点:将整个文档读入内存中,保留了过多的不须要的节点,浪费内存和空间。 函数

使用场合:一旦读入文档,还须要屡次对文档进行操做,而且在硬件资源充足的状况下(内存,CPU)。 性能

为了解决DOM解析存在的问题,就出现了SAX解析。其特色为this

优势:不用实现调入整个文档,占用资源少。尤为在嵌入式环境中,如android,极力推荐使用SAX解析。 spa

缺点:不像DOM解析同样将文档长期驻留在内存中,数据不是持久的。若是事件事后没有保存数据,数据就会丢失。 .net

使用场合:机器有性能限制。 xml

SAX解析XML文档采用事件驱动模式。什么是事件驱动模式?它将XML文档转换成一系列的事件,由单独的事件处理器来决定如何处理。 对象

基于事件驱动的处理模式主要是基于事件源和事件处理器(或者叫监听器)来工做的。一个能够产生事件的对象叫作事件源,而一个能够针对事件作出响应的对象就被叫作事件处理器。

在SAX接口中,事件源是org.xml.sax包中的XMLReader,他经过parse()方法开始解析XML文档,并根据文档内容产生事件。而事件处理器则是org.xml.sax包中的ContentHandler、DTDHandler、ErrorHandler,以及EntityResolver这四个接口。他们分别处理事件源在解析过程当中产生不一样类的事件(其中DTDHandler为解析文档DTD时所用)。详细介绍以下表:

image

 

在上述四个接口中,最重要的就是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文档解析事件ContentHandler
public 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的实现.

下面经过一个RSS解析的例子说明handler的实现:

咱们先是本身见一个rss的xml文档,实现本地解析,新建的rss文档以下:
<?xml version="1.0" encoding="UTF-8"?>
    <channel>
        <title>RSS 解析练习</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>描述详细信息的</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>描述详细信息的</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>描述详细信息的</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>描述详细信息的</description>
            <pubDate>2012-01-09</pubDate>
        </item>

 

image

而后咱们能够创建两个实体类:

一、RSSFeed,与完整的xml文档相对应;

二、RSSItem,与item标签内的信息相对应。

这样在解析xml时,咱们就能够把解析出来的信息放到实体类里,而后直接操做实体类就能够了。下面给出代码:

RSSFeed.java

mport 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);
    }
    /**
     * 负责将一个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;
    }
    /**
     * 负责从RSSFeed类中生成列表所须要的数据
     * @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;
    }
}

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.代码以下:

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;
        }
    }
}

就上面的代码分析,实现一个ContentHandler通常要一下几个步骤:

一、声明一个类,继承DefaultHandler。DefaultHandler是一个基类,这个类里面简单实现了一个ContentHandler。咱们只须要重写里面的方法便可。

二、重写 startDocument() 和 endDocument(),通常解析将正式解析以前的一些初始化工做放到startDocument()里面,收尾的工做放到endDocument()里面。 

三、重写startElement(),XML解析器遇到XML里面的tag时就会调用这个函数。常常在这个函数内是经过localName俩进行判断而操做一些数据。

四、重写characters()方法,这是一个回调方法。解析器执行完startElement()后,解析完节点的内容后就会执行这个方法,而且参数ch[]就是节点的内容。这个例子里咱们根据currentstate的不一样,来判断当前那个tag的内容,并放到合适的实体类中。

五、重写endElement()方法,这个方法与startElement()相对应,解析完一个tag节点后,执行这个方法。再找个例子中,若是解析一个item结束,就将RSSIiem添加到RSSFeed中。

相关文章
相关标签/搜索