Android XML解析器 - SAX

继续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 plain copy print ?
  1. public List<River> parse(String xmlPath){  
  2.     List<River> rivers=null;  
  3.   
  4.         SAXParserFactory factory=SAXParserFactory.newInstance();  
  5.   
  6.         try {  
  7.   
  8.             SAXParser parser=factory.newSAXParser();  
  9.   
  10.             //获取事件源  
  11.   
  12.             XMLReader xmlReader=parser.getXMLReader();  
  13.   
  14.             //设置处理器  
  15.   
  16.             RiverHandler handler=new RiverHandler();  
  17.   
  18.             xmlReader.setContentHandler(handler);  
  19.   
  20.             //解析xml文档  
  21.   
  22.             //xmlReader.parse(new InputSource(new URL(xmlPath).openStream()));  
  23.   
  24.             xmlReader.parse(new InputSource(this.context.getAssets().open(xmlPath)));  
  25.   
  26.             rivers=handler.getRivers();      
  27.   
  28.         } catch (ParserConfigurationException e) {  
  29.   
  30.             // TODO Auto-generated catch block  
  31.   
  32.             e.printStackTrace();  
  33.   
  34.         } catch (SAXException e) {  
  35.   
  36.             // TODO Auto-generated catch block  
  37.   
  38.             e.printStackTrace();  
  39.   
  40.         } catch (IOException e) {  
  41.   
  42.             e.printStackTrace();  
  43.   
  44.         }  
  45.   
  46.           
  47.   
  48.         return rivers;  
  49.   
  50.     }  

重点在于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();
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值