转载自  XML解析(一),SAX解析XML

一、概述

 SAX,全称Simple API for XML,是一种以事件驱动的XMl API,是XML解析的一种新的替代方法,解析XML常用的还有DOM解析,PULL解析(Android特有),SAX与DOM不同的是它边扫描边解析,自顶向下依次解析,由于边扫描边解析,所以它解析XML具有速度快,占用内存少的优点,对于Android等CPU资源宝贵的移动平台来说是一个巨大的优势。

  • SAX的优点:

    1. 解析速度快
    2. 占用内存少
  • SAX的缺点:

    1. 无法知道当前解析标签(节点)的上层标签,及其嵌套结构,仅仅知道当前解析的标签的名字和属性,要知道其他信息需要程序猿自己编码
    2. 只能读取XML,无法修改XML
    3. 无法随机访问某个标签(节点)
  • SAX解析适用场合 
    1. 对于CPU资源宝贵的设备,如Android等移动设备
    2. 对于只需从xml读取信息而无需修改xml

二、SAX解析的步骤

解析步骤很简单,可分为以下四个步骤

  1. 得到xml文件对应的资源,可以是xml的输入流,文件和uri
  2. 得到SAX解析工厂(SAXParserFactory
  3. 由解析工厂生产一个SAX解析器(SAXParser
  4. 传入输入流和handler给解析器,调用parse()解析

知道了SAX解析的优缺点和解析步骤,下面我们通过一个简单的Demo学习一下SAX解析XML

三、SAX解析实战

新建一个Android工程叫SaxParseXmlDemo,将sax.jar下载放到工程的lib下面并添加到构建路径中,为了方便,我先将工程的目录结构列一下:

1、新建一个xml文件叫users.xml

<?xml version="1.0" encoding="UTF-8"?>
<users><user id="1"><name>毕向东</name><password>bxd123</password></user><user id="2"><name>韩顺平</name><password>hsp123</password></user><user id="3"><name>马士兵</name><password>msb123</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):标签(节点)解析开始时调用

    1. uri:xml文档的命名空间
    2. localName:标签的名字
    3. qName:带命名空间的标签的名字
    4. attributes:标签的属性集
  • characters(char[] ch, int start, int length):解析标签的内容的时候调用

    1. ch:当前读取到的TextNode(文本节点)的字节数组
    2. start:字节开始的位置,为0则读取全部
    3. length:当前TextNode的长度
  • endElement(String uri, String localName, String qName):标签(节点)解析结束后调用
  • endDocument():文档解析结束后调用,该方法只会调用一次

 重写startDocument(),我们在这里初始化User集合,该集合用来存放解析出来的user

Log.e("startDocument", "startDocument()");
users = new ArrayList<User>();

 重写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 List<User> 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 ArrayList<User>();}/*** 节点解析开始调用* @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 List<User> getUsers() {return users;}
}

4、新建一个类XmlParseUtils.java,写一个方法进行xml解析

public static List<User> 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的根节点下面添加一个instrumentation

      <instrumentation android:targetPackage="com.example.saxparsexmldemo" android:name="android.test.InstrumentationTestRunner"></instrumentation>
    • 在 AndroidManifest.xml的application节点下面添加uses-library

      <uses-library android:name="android.test.runner"/>
  • com.example.saxparsexmldemo下面新建一个测试类SAXParseXmlTest.java,写一个测试方法
    public void testSAXgetUsers() throws Exception{List<User> users = XmlParseUtils.getUsers();for(User user : users){Log.e(TAG, "name:"+user.getName());Log.e(TAG, "password:"+user.getPassword());}}

 OK,右键该类的testSAXgetUsers,Run 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事件,这个过程可以理解为一个不断递归的过程。

OK,SAX解析原理分析完了,最后用一句话描述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

XML解析(一),SAX解析XML相关推荐

  1. XML的概述,.Dom4解析和SAX解析

    1.什么是XML XML 指可扩展标记语言(EXtensible Markup Language) html(hyper text markup langauge) XML 是一种标记语言,很类似 H ...

  2. Java:XML之JavaSE SAX解析

    XML之JavaSE SAX解析 概述 SAX(Simple API For XML)是一个公共的基于事件的XML文档解析标准,能够通过一个简单的.快速的方法来对XML文档进行处理,和DOM相比它所占 ...

  3. Java解析XML(DOM解析和SAX解析)

    前言:在程序中访问和操作XML文件一般有两种模型:DOM(文档对象模型)和流模型:在本篇文章中分别对应DOM解析和SAX解析. 目录 1 .DOM解析与SAX解析的相关知识点 1.1 DOM 1.2 ...

  4. Android[中级教程]第六章 XML解析之SAX解析器

    Android[中级教程]第六章 XML解析之SAX解析器 分类: Android中级2011-10-06 01:52 125人阅读 评论(1) 收藏 举报 接上一章,这一章我们就来学习SAX解析器, ...

  5. XML的SAX解析以及DOM解析和SAX解析区别

    前言: XML解析工具 DOM解析原理:1)JAXP (oracle-Sun公司官方)2)JDOM工具(非官方)3)Dom4J工具(非官方)三大框架(默认读取xml的工具就是Dom4j)....... ...

  6. UI一揽子计划 15 (XML的解析方式 (SAX解析/ DOM解析) 、JSON 解析)

    一. XML的SAX解析 1).SAX解析   1. 遵守协议      @interfaceRootViewController ()<NSXMLParserDelegate> 2. 声 ...

  7. JavaWeb-05 XML基础(Dom解析和Sax解析)

    JavaWeb-05 JavaWeb-XML基础(Dom解析和Sax解析) 一.XML的概述(了解) eXtensible Markup Language 可扩展标记语言 宗旨是传输数据,而非显示数据 ...

  8. DOM解析和SAX解析的区别

    DOM解析和SAX解析的区别 博客分类: XML DOM SAX  DOM解析和SAX解析的区别 No 区 别 DOM解析 SAX解析 1 操作 将所有文件读取到内存中形成DOM树,如果文件量过大,则 ...

  9. 对xml文件的sax解析(增删改查)之一

    crud(增删改查): c:creat r:retrieve u:update d:delete 以下笔记来自于韩顺平老师的讲解. 现在是用java来操作. 第一步:新建java工程.file-new ...

  10. XML解析---DOM解析和SAX解析

    XML是一种通用的数据交换格式,它的平台无关性.语言无关性.系统无关性.给数据集成与交互带来了极大的方便.XML在不同的语言环境中解析方式都是一样的,只不过实现的语法不同而已. 对于XML的解析,还有 ...

最新文章

  1. python与c语言在语法上的区别-python和c语言的区别是什么
  2. 什么是 Webhook?
  3. mysql 5.6开启binlog_docker快速入门02——在docker下开启mysql5.6 binlog日志
  4. mybatis中foreach标签详解
  5. linux杂七杂八整理
  6. 高等数学上-赵立军-北京大学出版社-题解-练习2.4
  7. 八、计数排序及其应用分析
  8. 【CodeForces - 270C】Magical Boxes (思维,进制,有坑)
  9. linux下软件发布,Linux Kernel 5.12发布下载,附新特性及新功能介绍
  10. android动画之布局动画,Android动画--布局动画 LayoutAnimation
  11. vc 文件总行数_Spark中的文件源(上)—— Spark的文件组织方式
  12. 、nohup 不中断后台执行
  13. 服务器配置文档模板,服务器配置模板
  14. Pytorch 实现手写数字识别
  15. 类似组卷网实现快速组卷功能,实现试题,试卷,课件快速录入、搜索、分类查询,支持mathtype和latex2word。
  16. 猜数字小游戏,超级简单就可以实现哦
  17. Windows 下使用 grub2 制作美观的维护U盘
  18. 实验3 微程序控制器实验
  19. 苦战金三银四:580道最全面的BATJ大厂Java面试真题分享
  20. 南京邮电大学CTF——WEB

热门文章

  1. 7-35 城市间紧急救援 (25 分)(思路加详解)
  2. [JavaWeb-CSS]CSS扩展选择器
  3. [C++STL]常用遍历算法
  4. Go中new和make的区别
  5. java 操作日志设计_日志系统新贵 Loki,确实比笨重的ELK轻
  6. #2693. jzptab
  7. 牛客练习赛73 D 离别(线段树+右端点排序离线查询)
  8. Atcoder ARC062F - AtCoDeerくんとグラフ色塗り / Painting Graphs with AtCoDeer
  9. Delete Edges
  10. Help Jimmy POJ - 1661