无锡建行网站,上海有色金属门户网站,公司网站空间,网站全能空间转载自 XML解析#xff08;一#xff09;#xff0c;SAX解析XML一、概述SAX#xff0c;全称Simple API for XML#xff0c;是一种以事件驱动的XMl API#xff0c;是XML解析的一种新的替代方法#xff0c;解析XML常用的还有DOM解析#xff0c;PULL解析#xff08;Andr…转载自 XML解析一SAX解析XML一、概述SAX全称Simple API for XML是一种以事件驱动的XMl API是XML解析的一种新的替代方法解析XML常用的还有DOM解析PULL解析Android特有SAX与DOM不同的是它边扫描边解析自顶向下依次解析由于边扫描边解析所以它解析XML具有速度快占用内存少的优点对于Android等CPU资源宝贵的移动平台来说是一个巨大的优势。
SAX的优点解析速度快占用内存少SAX的缺点无法知道当前解析标签节点的上层标签及其嵌套结构仅仅知道当前解析的标签的名字和属性要知道其他信息需要程序猿自己编码只能读取XML无法修改XML无法随机访问某个标签节点SAX解析适用场合 对于CPU资源宝贵的设备如Android等移动设备对于只需从xml读取信息而无需修改xml
二、SAX解析的步骤
解析步骤很简单可分为以下四个步骤
得到xml文件对应的资源可以是xml的输入流文件和uri得到SAX解析工厂SAXParserFactory由解析工厂生产一个SAX解析器SAXParser传入输入流和handler给解析器调用parse()解析
知道了SAX解析的优缺点和解析步骤下面我们通过一个简单的Demo学习一下SAX解析XML
三、SAX解析实战
新建一个Android工程叫SaxParseXmlDemo将sax.jar下载放到工程的lib下面并添加到构建路径中为了方便我先将工程的目录结构列一下1、新建一个xml文件叫users.xml
?xml version1.0 encodingUTF-8?
usersuser id1name毕向东/namepasswordbxd123/password/useruser id2name韩顺平/namepasswordhsp123/password/useruser id3name马士兵/namepasswordmsb123/password/user
/users
2、新建一个JavaBean
package com.example.saxparsexmldemo;public class User {private long id;private String name;private String password;public long getId() {return id;}public void setId(long id) {this.id id;}public String getName() {return name;}public void setName(String name) {this.name name;}public String getPassword() {return password;}public void setPassword(String password) {this.password password;}
}
3、新建一个类XmlParseHandler.java该类需要继承DefaultHandler或者实现ContentHandler接口这里我们通过继承DefaultHandler实现了ContentHandler接口的方式该类是SAX解析的核心所在我们要重写以下几个我们关心的方法。
startDocument()文档解析开始时调用该方法只会调用一次startElement(String uri, String localName, String qName, Attributes attributes):标签节点解析开始时调用urixml文档的命名空间localName标签的名字qName带命名空间的标签的名字attributes标签的属性集characters(char[] ch, int start, int length)解析标签的内容的时候调用ch当前读取到的TextNode(文本节点)的字节数组start字节开始的位置为0则读取全部length当前TextNode的长度endElement(String uri, String localName, String qName)标签节点解析结束后调用endDocument()文档解析结束后调用该方法只会调用一次重写startDocument()我们在这里初始化User集合该集合用来存放解析出来的user
Log.e(startDocument, startDocument());
users new ArrayListUser();重写startElement在startElement中先判断当前的标签是否user如果是user标签则说明接下来是一个user的信息所以我们新建一个User对象用来存放这个user的信息在这里我们得到当前user标签的id属性封装到user对象中。并记录当前的标签Log.e(startElement, localName -startElement());if (user.equals(localName)) { // 是一个用户for (int i 0; i attributes.getLength(); i) {Log.e(attributes, attribute_name attributes.getLocalName(i) attribute_value attributes.getValue(i));user new User();if(id.equals(attributes.getLocalName(i))){user.setId(Long.parseLong(attributes.getValue(i)));}}}currentTag localName; // 把当前标签记录下来重写characters在characters中解析出当前标签的内容如果当前标签为name标签则解析name标签的内容封装到当前User对象的name属性中如果当前标签为password标签则解析password标签的内容封装到当前User对象的password属性中String value new String(ch,start,length); // 将当前TextNode转换为StringLog.e(characters, value);if(name.equals(currentTag)){ // 当前标签为name标签该标签无子标签直接将上面获取到的标签的值封装到当前User对象中// 该节点为name节点user.setName(value);}else if(password.equals(currentTag)){ // 当前标签为password标签该标签无子标签直接将上面获取到的标签的值封装到当前User对象中// 该节点为password节点user.setPassword(value);}重写endElement在这个方法中判断当前是否是user标签的结束如果是user标签结束则这个user信息解析结束并将当前的User对象和当前的标签重置Log.e(endElement, localName -endElement());if(user.equals(localName)){users.add(user);user null;}currentTag null;重写endDocument这里直接给个空实现我们只需观察Log输出
Log.e(endDocument, -endDocument());
XmlParseHandler.java完整代码:
package com.example.saxparsexmldemo;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 android.util.Log;public class XmlParseHandler extends DefaultHandler {private ListUser users;private String currentTag; // 记录当前解析到的节点名称private User user; // 记录当前的user/*** 文档解析结束后调用*/Overridepublic void endDocument() throws SAXException {super.endDocument();Log.e(endDocument, -endDocument());}/*** 节点解析结束后调用* param uri : 命名空间的uri* param localName : 标签的名称* param qName : 带命名空间的标签名称*/Overridepublic void endElement(String uri, String localName, String qName)throws SAXException {super.endElement(uri, localName, qName);Log.e(endElement, localName -endElement());if(user.equals(localName)){users.add(user);user null;}currentTag null;}/*** 文档解析开始调用*/Overridepublic void startDocument() throws SAXException {super.startDocument();Log.e(startDocument, startDocument());users new ArrayListUser();}/*** 节点解析开始调用* param uri : 命名空间的uri* param localName : 标签的名称* param qName : 带命名空间的标签名称*/Overridepublic void startElement(String uri, String localName, String qName,Attributes attributes) throws SAXException {super.startElement(uri, localName, qName, attributes);Log.e(startElement, localName -startElement());if (user.equals(localName)) { // 是一个用户for (int i 0; i attributes.getLength(); i) {Log.e(attributes, attribute_name attributes.getLocalName(i) attribute_value attributes.getValue(i));user new User();if(id.equals(attributes.getLocalName(i))){user.setId(Long.parseLong(attributes.getValue(i)));}}}currentTag localName; // 把当前标签记录下来}Overridepublic void characters(char[] ch, int start, int length)throws SAXException {super.characters(ch, start, length); String value new String(ch,start,length); // 将当前TextNode转换为StringLog.e(characters, value);if(name.equals(currentTag)){ // 当前标签为name标签该标签无子标签直接将上面获取到的标签的值封装到当前User对象中// 该节点为name节点user.setName(value);}else if(password.equals(currentTag)){ // 当前标签为password标签该标签无子标签直接将上面获取到的标签的值封装到当前User对象中// 该节点为password节点user.setPassword(value);}}public ListUser getUsers() {return users;}
}
4、新建一个类XmlParseUtils.java写一个方法进行xml解析
public static ListUser getUsers() throws ParserConfigurationException, SAXException, IOException {// 加载文件返回文件的输入流InputStream is XmlParseUtils.class.getClassLoader().getResourceAsStream(users.xml);XmlParseHandler handler new XmlParseHandler();// 1. 得到SAX解析工厂SAXParserFactory saxParserFactory SAXParserFactory.newInstance();// 2. 让工厂生产一个sax解析器SAXParser newSAXParser saxParserFactory.newSAXParser();// 3. 传入输入流和handler解析newSAXParser.parse(is, handler);is.close();return handler.getUsers();}其中第三步也可以通过XMLReader来完成
XMLReader xmlReader newSAXParser.getXMLReader();
InputSource input new InputSource(is);
xmlReader.parse(input );
到这里SAX解析XML的代码完成了OK万事具备只欠测试了
这里我们用Android的单元测试只需完成调用XmlParseUtils的getUsers()方法测试输出即可
Android的单元测试需要连接模拟器或者手机
环境搭建在 AndroidManifest.xml的根节点下面添加一个instrumentationinstrumentation android:targetPackagecom.example.saxparsexmldemo android:nameandroid.test.InstrumentationTestRunner/instrumentation在 AndroidManifest.xml的application节点下面添加uses-libraryuses-library android:nameandroid.test.runner/在com.example.saxparsexmldemo下面新建一个测试类SAXParseXmlTest.java写一个测试方法public void testSAXgetUsers() throws Exception{ListUser users XmlParseUtils.getUsers();for(User user : users){Log.e(TAG, nameuser.getName());Log.e(TAG, passworduser.getPassword());}}OK右键该类的testSAXgetUsersRun As Android JUnit Test我们可以看到XML解析成功通过上面的这个小Demo的完成我们可以看到SAX解析代码不多也不难理解关键是Handler的几个方法的使用即遇到什么符号会触发什么事件以及触发过程掌握了SAX的事件触发那么就掌握了SAX解析XML,下面我们来分析一下SAX解析的原理或流程
四、SAX解析XML原理
在分析SAX解析原理之前我们先看一下上面的demo的日志输出以users.xml的解析过程为例结合上面的日志输出我们可以分析出SAX解析的流程
1、xml解析开始,startDocument被调用这个方法在整个xml解析过程中调用了一次所以我们可以在这个方法里面初为解析XML做一些准备比如初始化变量 2、解析每遇到一个标签都会经历startElement-characters-endElement这个过程即每一个标签都会触发startElement-characters-endElement 3、通过users这个根节点的解析user标签解析以及name,password标签解析过程我们知道user标签是users的子标签name和password标签是user标签的子标签我们知道当解析一个标签的时候如果该标签有子标签则先回调用该标签的startElement方法这里面可以先得到该标签的属性信息然后触发characters解析该标签的内容值然后子标签触发startElement-characters-endElement(子标签触发)最后该标签触发endElement该标签解析结束
下面画一个图让我们进一步理解SAX解析XML的原理:对上图做个简单说明当当前标签有子标签的时候该标签先触发characters然后子标签触发startElement-characters-endElement事件这个过程可以理解为一个不断递归的过程。
OKSAX解析原理分析完了最后用一句话描述SAX解析过程
startDocument-startElement-characters-endElement-endDocument
总结SAX解析XML具有解析速度快占用内存少对于Android等移动设备来说有巨大的优势深入了解SAX的事件触发机制是掌握SAX解析的关键掌握了SAX的事件触发就掌握了SAX解析XML
上面这篇文章由于个人理解如果有理解错的地方欢迎大家指出与君共勉一起进步。
Demo下载地址http://download.csdn.net/detail/ydxlt/9328309
下篇文章【XML解析二】DOM解析XML