Android开发里,解析xml是很常见的场景了,其中SAX解析与PULL解析是两种比较常用的解析方式。

PULL方式解析xml的文章可点这里:Android XML解析之PULL解析 简单示例讲解

除此之外还有DOM解析,这个不太常用,因为它需一次性把xml文件加载到内存里,如果是大文件的话,很占内存,影响性能。

而SAX(Simple API for XML)解析是事件驱动的流式解析方式,并不是把xml全部加载到内存,而是一边读取一边解析,不可暂停或者倒退,直到结束。在解析过程中,会判断当前的节点及内容,而触发不同的方法被调用。我们需要重写这些回调方法,实现自己的处理。

SAX解析的优点:占用内存少,速度快,如果对性能要求比较高,推荐使用。
缺点:不像DOM解析一样将文档长期驻留在内存中,数据不是持久的。如果事件过后没有保存数据,数据就会丢失。

本篇文章,会用一个简单的例子示例下SAX解析的用法。

一、先准备一个简单的xml文件。

路径:res/raw/person_data.xml:

<?xml version="1.0" encoding="UTF-8"?><data>    <person id="5">        <name>Jackname>        <age>25age>    person>    <person id="20">        <name>Rosename>        <age>80age>    person>data>

二、新建一个Person类。

我们的目标是把上面的xml数据(两个person)转换成一个Person列表。
Person类如下,和xml里的属性一一对应:

public class Person {    private int id;    private String name;    private int age;    public int getId() {        return id;    }    public void setId(int id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }    @Override    public String toString() {        return "Person{" +                "id=" + id +                ", name='" + name + '\'' +                ", age=" + age +                '}';    }}

下面先看看如何用SAX方式解析上面的xml:

三、定义解析处理器。

我们需要继承自org.xml.sax.helpers.DefaultHandler类,实现一个解析处理器类。这个DefaultHandler类里比较重要的方法是下面几个,可以选择性的重写:
startDocument() —— 开始解析的时候被调用,可以在此方法里做一些初始化的准备工作。

startElement(String uri, String localName, String qName, Attributes attributes) —— 此方法在每次解析到一个开始节点的时候被调用。其中uri表示元素的命名空间;localName表示元素的本地名称(不带前缀);qName表示元素的限定名(带前缀);attrs表示元素的属性集合。

characters(char[] ch, int start, int length) —— 接收字符数据的通知。改方法用来处理在XML文件中读到的内容,第一个参数用来存放文件的内容,后面两个参数是读到的字符串在这个数组中的起始位置和长度。使用newSreing(ch,start,length)就可以获取内容。这个方法被调用的次数最多,包括换行符也会被当做内容而传进这个方法里。

endElement(String uri, String localName, String qName) —— 在每次遇到结束节点的时候被调用。其中uri表示元素的命名空间;localName表示元素的本地名称(不带前缀);name表示元素的限定名(带前缀)。

endDocument() —— 解析结束的时候被调用。

下面看代码:

import android.util.Log;import org.xml.sax.Attributes;import org.xml.sax.SAXException;import org.xml.sax.helpers.DefaultHandler;import java.util.ArrayList;import java.util.List;public class MyContentHandler extends DefaultHandler {    private static final String TAG = "TAG";    private String currentNodeName; //当前的节点名称    private List<Person> personList;    private Person person; // 当前正在解析的Person// 这里定义这个方法是为了把解析结果提供出去    public List<Person> getPersonList() {        return personList;    }    @Override    public void startDocument() throws SAXException {        Log.d(TAG, "startDocument() called");        personList = new ArrayList<>();    }    @Override    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {        Log.d(TAG, "startElement() called with: uri = [" + uri + "], localName = [" + localName + "], qName = [" + qName + "]");        // 记录当前节点名称        currentNodeName = localName;        // 每次开始节点为“person”的话,就要开始解析一个新的person对象了        if (localName.equals("person")) {            person = new Person();            // 获取person节点的id属性            person.setId(Integer.parseInt(attributes.getValue("id")));        }    }    @Override    public void characters(char[] ch, int start, int length) throws SAXException {        Log.d(TAG, "characters() called with: ch = [" + String.valueOf(ch) + "], start = [" + start + "], length = [" + length + "]");        if (currentNodeName != null) {            String data = new String(ch, start, length);            if (currentNodeName.equals("name")) {                person.setName(data);            } else if (currentNodeName.equals("age")) {                person.setAge(Integer.parseInt(data));            }        }    }    @Override    public void endElement(String uri, String localName, String qName) throws SAXException {        Log.d(TAG, "endElement() called with: uri = [" + uri + "], localName = [" + localName + "], qName = [" + qName + "]");        // 每次结束节点为“person”的话,则一个person对象的解析结束了        if (localName.equals("person")) {            personList.add(person);            person = null;        }        // 把当前节点重置        currentNodeName = null;    }    @Override    public void endDocument() throws SAXException {        Log.d(TAG, "endDocument() called");    }}

接下来,我们就可以使用这个解析处理器进行解析了。

四、使用解析处理器进行xml解析。

怎么用呢?如下面的方法:

    public static void saxParseXml(Context context) {        try {            SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();            XMLReader reader = saxParser.getXMLReader();            // 这里用到了我们自定义的解析执行器            MyContentHandler contentHandler = new MyContentHandler();            reader.setContentHandler(contentHandler);            // 读取xml文件,执行解析            reader.parse(new InputSource(context.getResources().openRawResource(R.raw.person_data)));            // 得到解析结果            List<Person> personList = contentHandler.getPersonList();            Log.d("TAG", "parse result: " + personList);        } catch (SAXException e){        } catch (ParserConfigurationException e) {        } catch (IOException e) {        }    }

调用上面的方法即可解析res/raw/person_data.xml文件并得到Person类列表了。
为了更直观地查看解析的流程及解析类的几个方法的参数,我都加上了log。

我是在某个按钮的点击事件里调用这个方法的,运行后打印如下。可对照看到各个方法的执行顺序。

D/TAG: startDocument() calledD/TAG: startElement() called with: uri = [], localName = [data], qName = [data]    characters() called with: ch = [    ], start = [0], length = [1]    characters() called with: ch = [    ], start = [0], length = [4]D/TAG: startElement() called with: uri = [], localName = [person], qName = [person]    characters() called with: ch = [       ], start = [0], length = [1]    characters() called with: ch = [        ], start = [0], length = [8]    startElement() called with: uri = [], localName = [name], qName = [name]    characters() called with: ch = [Jack    ], start = [0], length = [4]D/TAG: endElement() called with: uri = [], localName = [name], qName = [name]    characters() called with: ch = [    ack    ], start = [0], length = [1]    characters() called with: ch = [        ], start = [0], length = [8]    startElement() called with: uri = [], localName = [age], qName = [age]    characters() called with: ch = [25      ], start = [0], length = [2]D/TAG: endElement() called with: uri = [], localName = [age], qName = [age]    characters() called with: ch = [    5      ], start = [0], length = [1]    characters() called with: ch = [        ], start = [0], length = [4]    endElement() called with: uri = [], localName = [person], qName = [person]    characters() called with: ch = [           ], start = [0], length = [1]    characters() called with: ch = [        ], start = [0], length = [4]    startElement() called with: uri = [], localName = [person], qName = [person]D/TAG: characters() called with: ch = [           ], start = [0], length = [1]    characters() called with: ch = [        ], start = [0], length = [8]    startElement() called with: uri = [], localName = [name], qName = [name]    characters() called with: ch = [Rose    ], start = [0], length = [4]    endElement() called with: uri = [], localName = [name], qName = [name]    characters() called with: ch = [    ose    ], start = [0], length = [1]    characters() called with: ch = [        ], start = [0], length = [8]    startElement() called with: uri = [], localName = [age], qName = [age]    characters() called with: ch = [80      ], start = [0], length = [2]    endElement() called with: uri = [], localName = [age], qName = [age]D/TAG: characters() called with: ch = [    0      ], start = [0], length = [1]    characters() called with: ch = [        ], start = [0], length = [4]    endElement() called with: uri = [], localName = [person], qName = [person]    characters() called with: ch = [           ], start = [0], length = [1]    endElement() called with: uri = [], localName = [data], qName = [data]D/TAG: endDocument() called

当然最后也得到了Person列表,打印如下:

    parse result: [Person{id=5, name='Jack', age=25}, Person{id=20, name='Rose', age=80}]

这是PULL方式解析xml的文章链接,可以对照查看:
Android XML解析之PULL解析 简单示例讲解

更多相关文章

  1. 手把手带你搭建 Elasticsearch 集群
  2. Android(安卓)微信支付成功后返回App没有走回调
  3. Android(安卓)IntentService的使用和源码分析
  4. Android使用C/C++来保存密钥
  5. 百度地图(1)
  6. Android4.0 x86试玩
  7. 异步AsyncTask的使用与原理分析
  8. Android(安卓)通过JNI C++进行MD5加密
  9. Handler消息机制深入学习

随机推荐

  1. Android studio使用心得(二)--- 打包签名
  2. android.os包中一些类的使用
  3. Android自定义view七时间轴(三)---纵向的图
  4. 打开android 系统联系人
  5. android解决部分手机无法通过uri获取到相
  6. Android判断App是否在前台运行
  7. Android(安卓)OpenGL ES材质及光照示例
  8. 跑马灯结合TextSwitcher实现系统公告栏
  9. mac 搭建APK反编译环境
  10. android 实现Iphone效果Switch,