欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

详解android使用SAX解析XML文件

程序员文章站 2024-03-02 19:48:46
解析xml的方式有很多种,大家比较熟悉的可能就是dom解析。 dom(文件对象模型)解析:解析器读入整个文档,然后构建一个驻留内存的树结构,然后代码就可以根据dom接口来...

解析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时所用)。详细介绍如下表:详解android使用SAX解析XML文件

在上述四个接口中,最重要的就是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>

建好后,我们命名为rssxml.xml,然后放到项目的根目录下:
详解android使用SAX解析XML文件

然后我们可以建立两个实体类:

1、rssfeed,与完整的xml文档相对应;

2、rssitem,与item标签内的信息相对应。

这样在解析xml时,我们就可以把解析出来的信息放到实体类里,然后直接操作实体类就可以了。下面给出代码:

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);
  }
  
  /**
   * 负责将一个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;
  }
  
}

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

就上面的代码分析,实现一个contenthandler一般要一下几个步骤:

1、声明一个类,继承defaulthandler。defaulthandler是一个基类,这个类里面简单实现了一个contenthandler。我们只需要重写里面的方法即可。

2、重写 startdocument() 和 enddocument(),一般解析将正式解析之前的一些初始化工资放到startdocument()里面,收尾的工作放到enddocument()里面。

3、重写startelement(),xml解析器遇到xml里面的tag时就会调用这个函数。经常在这个函数内是通过localname俩进行判断而操作一些数据。

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

5、重写endelement()方法,这个方法与startelement()相对应,解析完一个tag节点后,执行这个方法。再找个例子中,如果解析一个item结束,就将rssiiem添加到rssfeed中。

最后我们实现一个activity来展现解析的结果:

 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 {
// 这里我们实现了本地解析,所以注掉了这个取网络数据的。
//      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("访问失败...");
      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);
  }
}

展示下运行结果:
详解android使用SAX解析XML文件
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。