你真的了解android中的SAX解析吗?
你真的了解android中的SAX解析吗?
首先,先介绍下在Android当中解析服务器端传过来的XML数据,通常是这三种方式,分别是DOM、SAX以及PULL。
接下来,我们对DOM和PULL做一个简单的介绍,这篇博客着重来阐述SAX。
一、DOM解析
DOM将整个xml看成是一个树状的结构,在解析的时候,会将整个xml文件加载到我们的内存当中,然后通过DOM提供的API来对我们的xml数据进行解析,这种方式解析xml非常的方便,并且我们可以通过某个节点访问到其兄弟或者是父类、子类节点。
通过DOM解析xml的好处就是,我们可以随时访问到某个节点的相邻节点,并且对xml文档的插入也非常的方便,不好的地方就是,其会将整个xml文档加载到内存中,这样会大大的占用我们的内存资源,对于手机来说,内存资源是非常非常宝贵的,所以在手机当中,通过DOM这种方式来解析数据量不大的xml。
二、SAX解析
- 什么是SAX?
SAX,全称是Simple API for XML,既是指一种接口,也是指一个软件包。
作为接口,SAX是事件驱动型XML解析的一个标准接口。所谓的事件型驱动也就是说,它并不需要解析完整个文档,而是按内容顺序进行文档解析。
- SAX工作原理
SAX的工作原理简单地说就是对文档进行顺序扫描,当扫描到文档(document)开始与结束、元素(element)开始与结束等地方时通知事件处理函数,由事件处理函数做相应的动作,然后继续同样的扫描,直至文档结束。
大多数SAX实现都会产生以下类型的事件:
- (1)在文档的开始和结束时触发文档处理事件
- (2)在文档内每一xml元素接受解析的前后触发元素事件。
- (3)任何元数据通常都由单独的事件交付。
- (4)在处理文档的DTD或者Schema时产生DTD或Schema事件。
- (5)产生错误事件用来通知主机应用程序解析错误。
可以看到SAX模型主要分为三大块,首先是SAX解析器工厂(当然使用的是工厂模式)产生一个SAX解析器,SAX解析器产生一个SAXReader一个读取文件的对象,在这个SAXReader上有一些接口,比如有处理内容的接口、错误的接口、DTD接口、处理实体的接口等。SAXReader对xml进行逐行的读取,然后在对应的事件接口进行相应的处理(主要是对接口中方法的重写)。
三、SAX常用接口介绍
ContentHandler接口:ContentHandler是Java类包中一个特殊的SAX接口,该接口中封装了一些对事件处理的方法,当xml解析器开始解析xml输入文档时,它会遇到某些特殊的事件,比如文档的开头和结束、元素的开头和结束、以及元素中的字符数据等事件。当遇到这些事件时,xml解析会调用ContentHandler接口中相应的方法来响应事件。
startDocument()
当遇到文档的开头的时候,调用这个方法,主要是做一些预处理的工作,比如对自己定义的列表进行初始化等。
endDocument()
和上面的方法相对应,当文档结束的时候,调用这个方法,这个方法我感觉基本没啥作用啊。
startElement(String namespaceURI, String localName, String qName, Attributes atts)
当读到一个开始标签的时候,会触发这个方法。namespaceURI就是命名空间,localName是不带命名空间前缀的标签名,qName是带命名空间前缀的标签名。通过atts可以得到所有的属性名和相应的值,一般情况用qName多些。
endElement(String uri, String localName, String qName)
这个方法和上面的方法相对应,在遇到结束标签的时候,调用这个方法。
characters(char[] ch, int start, int length)
这个方法用来处理在XML文件中读到的内容,第一个参数用于存放文件的内容,后面两个参数是读到的字符串在这个数组中的起始位置和长度,使用new String(ch,start,length)就可以获取内容。
四、代码来体验一把
代码块
首先向服务请求数据,最后将服务返回的数据流进行解析:
InputStream soapResponseData = postMethod.getResponseBodyAsStream();
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
MyContentHandler parseXml = new MyContentHandler(); // 实例化解析类
parser.parse(soapResponseData, parseXml); // 两个参数,一个是输入流,一个是解析类对象。
在MyContentHandler类中具体来对xml的数据流进行解析:
public class MyContentHandler extends DefaultHandler {
public static List list; //将解析的历史账单账单详情中详细的数据放在一个List里面 public static List listone;//历史账单中的的显示 包括 下单日期 总价格public static List listtwo;//历史账单中的的显示 包括 打球日期 总价格private Bill bill;private BillOne billone;private BillTwo billtwo;private String tagName; //存放元素名称 public List getList() { return list; } @Override public void startDocument() throws SAXException { list = new ArrayList(); //开始解析时实例化list listone=new ArrayList();; listtwo = new ArrayList(); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if (qName.equals("BillItem")) { //每个元素都是一个element bill = new Bill(); // 获取节点上的id属性值 // billmodel.setId((attributes.getValue(0))); //属性和文本有所不同 } if (qName.equals("Bills")) { billone = new BillOne(); } if (qName.equals("BillList")) { billtwo = new BillTwo(); } this.tagName = qName; } @Override public void endElement(String uri, String localName, String qName) throws SAXException { if (qName.equals("BillItem")) { list.add(this.bill); } if (qName.equals("Bills")) { this.billone.setListtwo(listtwo); listone.add(this.billone); listtwo = new ArrayList();//每次放入列表后,一定要进行初始化清空列表中的数据!!!! } if (qName.equals("BillList")) { this.billtwo.setList(list); listtwo.add(this.billtwo); this.billtwo=null; list = new ArrayList(); } this.tagName = null; //将标志置为null } @Override public void characters(char[] ch, int start, int length) throws SAXException { if (this.tagName != null) { //如果tagName不为null,之前看过一篇文章,元素前的空白处也是会被解析的。 String data = new String(ch, start, length); //开始存放数据 if (this.tagName.equals("ServerName")) { this.bill.setServerName(data); } else if (this.tagName.equals("ServerPrice")) { this.bill.setServerPrice(data); } else if (this.tagName.equals("Amount")) { this.bill.setAmount(data); }else if (this.tagName.equals("SumDue")) { this.bill.setSumDue(data); }else if (this.tagName.equals("OrderDate")) { this.billone.setOrderDate(data); }else if (this.tagName.equals("TotalPrice")) { this.billone.setTotalPrice(data); }else if (this.tagName.equals("Place")) { this.billtwo.setPlace(data); }else if (this.tagName.equals("SumMoney")) { this.billtwo.setSumMoney(data); } } }
}
三、PULL解析
pull解析和sax解析类似,都是基于事件流的方式,Pull解析器和SAX解析器虽有区别但也有相似性。他们的区别为:SAX解析器的工作方式是自动将事件推入注册的事件处理器进行处理,因此你不能控制事件的处理主动结束;
而Pull解析器的工作方式为允许你的应用程序代码主动从解析器中获取事件,正因为是主动获取事件,因此可以在满足了需要的条件后不再获取事件,结束解析。这是他们主要的区别,换句话说就是pull解析可以随意获取xml中的部分数据而不需要进行整个文档的解析,DOM和SAX都是对整个文档进行解析。
而他们的相似性在运行方式上,Pull解析器也提供了类似SAX的事件(开始文档START_DOCUMENT和结束文档END_DOCUMENT,开始元素START_TAG和结束元素END_TAG,遇到元素内容TEXT等),但需要调用next() 方法提取它们(主动提取事件)。
更多相关文章
- 【自定义控件】android事件分发机制
- android 事件机制与事件监听(一)
- Android异步加载全解析之引入二级缓存
- Android(安卓)让人又爱又恨的触摸机制(二)
- Android(安卓)UI事件处理
- Android中Fragment碎片解析
- Android(安卓)init 启动过程分析(一)
- Android(安卓)Okhttp 断点续传面试解析
- Router—一个高效,使用方便,基于动态代理实现的Android事件总线库