您的位置:首页 > 移动开发 > Android开发

Android XML解析器 - SAX

2015-12-24 18:18 417 查看
继续DOM解析XML


SAX解析器

 SAX(Simple API for XML)解析器是一种基于事件的解析器,事件驱动的流式解析方式是,从文件的开始顺序解析到文档的结束,不可暂停或倒退。它的核心是事件处理模式,

主要是围绕着事件源以及事件处理器来工作的。当事件源产生事件后,调用事件处理器相应的处理方法,一个事件就可以得到处理。在事件源调用事件处理器中特定方法的时候,

还要传递给事件处理器相应事件的状态信息,这样事件处理器才能够根据提供的事件信息来决定自己的行为。  

  SAX解析器的优点是解析速度快,占用内存少。非常适合在Android移动设备中使用。

SAX的工作原理:SAX的工作原理简单地说就是对文档进行顺序扫描,

当扫描到文档(document)开始与结束、元素(element)开始与结束、文档(document)结束等地方时通知事件处理函数,由事件处理函数做相应动作,然后继续同样的扫描,直至文档结束。

 在SAX接口中,事件源是org.xml.sax包中的XMLReader,它通过parser()方法来解析XML文档,并产生事件。

事件处理器是org.xml.sax包中ContentHander、DTDHander、ErrorHandler,以及EntityResolver这4个接口。XMLReader通过相应事件处理器注册方法setXXXX()来完成的与ContentHander、DTDHander、ErrorHandler,以及EntityResolver这4个接口的连接。

常用的SAX接口和类:

 Attrbutes:用于得到属性的个数、名字和值。  

 ContentHandler:定义与文档本身关联的事件(例如,开始和结束标记)。大多数应用程序都注册这些事件。

 DTDHandler:定义与DTD关联的事件。它没有定义足够的事件来完整地报告DTD。如果需要对DTD进行语法分析,请使用可选的DeclHandler。

 DeclHandler是SAX的扩展。不是所有的语法分析器都支持它。

 EntityResolver:定义与装入实体关联的事件。只有少数几个应用程序注册这些事件。

 ErrorHandler:定义错误事件。许多应用程序注册这些事件以便用它们自己的方式报错。

 DefaultHandler:它提供了这些接LI的缺省实现。在大多数情况下,为应用程序扩展DefaultHandler并覆盖相关的方法要比直接实现一个接口更容易。

 详见下表:



 

 

 可知,我们需要XmlReader 以及DefaultHandler来配合解析xml。

 下面是SAX的解析流程:

  


 

采用SAX解析时具体处理步骤是:

1 创建SAXParserFactory对象

2 根据SAXParserFactory.newSAXParser()方法返回一个SAXParser解析器

3 根据SAXParser解析器获取事件源对象XMLReader

4 实例化一个DefaultHandler对象

5 连接事件源对象XMLReader到事件处理类DefaultHandler中

6 调用XMLReader的parse方法从输入源中获取到的xml数据

7 通过DefaultHandler返回我们需要的数据集合。

代码如下:

[java] view
plaincopyprint?

public List<River> parse(String xmlPath){  

    List<River> rivers=null;  

  

        SAXParserFactory factory=SAXParserFactory.newInstance();  

  

        try {  

  

            SAXParser parser=factory.newSAXParser();  

  

            //获取事件源  

  

            XMLReader xmlReader=parser.getXMLReader();  

  

            //设置处理器  

  

            RiverHandler handler=new RiverHandler();  

  

            xmlReader.setContentHandler(handler);  

  

            //解析xml文档  

  

            //xmlReader.parse(new InputSource(new URL(xmlPath).openStream()));  

  

            xmlReader.parse(new InputSource(this.context.getAssets().open(xmlPath)));  

  

            rivers=handler.getRivers();      

  

        } catch (ParserConfigurationException e) {  

  

            // TODO Auto-generated catch block  

  

            e.printStackTrace();  

  

        } catch (SAXException e) {  

  

            // TODO Auto-generated catch block  

  

            e.printStackTrace();  

  

        } catch (IOException e) {  

  

            e.printStackTrace();  

  

        }  

  

          

  

        return rivers;  

  

    }  

重点在于DefaultHandler对象中对每一个元素节点,属性,文本内容,文档内容进行处理。

 

前面说过DefaultHandler是基于事件处理模型的,基本处理方式是:当SAX解析器导航到文档开始标签时回调startDocument方法,导航到文档结束标签时回调endDocument方法。当SAX解析器导航到元素开始标签时回调startElement方法,导航到其文本内容时回调characters方法,导航到标签结束时回调endElement方法。

 

根据以上的解释,我们可以得出以下处理xml文档逻辑:

1:当导航到文档开始标签时,在回调函数startDocument中,可以不做处理,当然你可以验证下UTF-8等等。

2:当导航到rivers开始标签时,在回调方法startElement中可以实例化一个集合用来存贮list,不过我们这里不用,因为在构造函数中已经实例化了。

3:导航到river开始标签时,就说明需要实例化River对象了,当然river标签中还有name ,length属性,因此实例化River后还必须取出属性值,attributes.getValue(NAME),同时赋予river对象中,同时添加为导航到的river标签添加一个boolean为真的标识,用来说明导航到了river元素。

4:当然有river标签内还有子标签(节点),但是SAX解析器是不知道导航到什么标签的,它只懂得开始,结束而已。那么如何让它认得我们的各个标签呢?当然需要判断了,于是可以使用回调方法startElement中的参数String localName,把我们的标签字符串与这个参数比较下,就可以了。我们还必须让SAX知道,现在导航到的是某个标签,因此添加一个true属性让SAX解析器知道。

5:它还会导航到文本内标签,(就是<img></img>里面的内容),回调方法characters,我们一般在这个方法中取出就是<img></img>里面的内容,并保存。 6:当然它是一定会导航到结束标签</river> 或者</rivers>的,如果是</river>标签,记得把river对象添加进list中。如果是river中的子标签</introduction>,就把前面设置标记导航到这个标签的boolean标记设置为false. 按照以上实现思路,可以实现如下代码:

package com.andy.utils.parsexml;

import java.util.ArrayList;
import java.util.List;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import com.andy.utils.LG;

public class RiverHandler extends DefaultHandler {
List<River> list;
River river = null;
int currentState = 0;
final int intr = 1;
final int url = 2;
public List<River> getList() {
return list;
}
/*
* 文档开始通知
*/
@Override
public void startDocument() throws SAXException {
// TODO Auto-generated method stub
list = new ArrayList<River>();
}
/*
* 标签开始通知
*/
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
// TODO Auto-generated method stub
LG.i(getClass(), "[startElement]: "+uri+","+localName+","+qName);
if(localName.equals("rivers")){
currentState = 0;
return;
}
if(localName.equals("river")){
river = new River();
for(int i = 0; i<attributes.getLength();i++){
LG.i(getClass(), "[startElement-attributes]: "+attributes.getLocalName(i));
if (attributes.getLocalName(i).equals("name")) {
river.setName(attributes.getValue(i));
} else if (attributes.getLocalName(i).equals("length")) {
river.setLength(Integer.parseInt(attributes.getValue(i)));
}
}
return;
}
if(localName.equals("introduction")){
LG.i(getClass(), "[startElement]: "+currentState+"->"+intr);
currentState = intr;
return;
}
if(localName.equals("imageurl")){
LG.i(getClass(), "[startElement]: "+currentState+"->"+url);
currentState = url;
return;
}
}
/*
* 接口字符块通知
*/
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
// TODO Auto-generated method stub
// super.characters(ch, start, length);
String theString = String.valueOf(ch, start, length).trim();
//or
//String theString = new String(ch, start, length).trim();
LG.i(getClass(), "[characters]: "+currentState+",-"+theString);

if(theString == null || theString.length() <= 0){
return;
}
switch(currentState){
case intr:
river.setIntroduction(theString);
LG.i(getClass(), "[characters]: "+currentState+"->"+0);
currentState = 0;
break;
case url:
river.setImageurl(theString);
LG.i(getClass(), "[characters]: "+currentState+"->"+0);
currentState = 0;
break;
default:
return;
}
}
/*
* 接收标签结束通知
*/
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
// TODO Auto-generated method stub
LG.i(getClass(), "[endElement]: "+uri+","+localName+","+qName);
if (localName.equals("river")){
list.add(river);
river = null;
}
}
/*
* 接收文档结束通知
*/
@Override
public void endDocument() throws SAXException {
// TODO Auto-generated method stub
super.endDocument();
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: