SAX解析XML文件采用事件驱动的方式进行,也就是说,SAX是逐行扫描文件,遇到符合条件的设定条件后就会触发特定的事件,回调你写好的事件处理程序。使用SAX的优势在于其解析速度较快,占用内存较少(相对于DOM而言)。而且SAX在解析文件的过程中得到自己需要的信息后可以随时终止解析,并不一定要等文件全部解析完毕。凡事有利必有弊,其劣势在于SAX采用的是流式处理方式,当遇到某个标签的时候,它并不会记录下以前所遇到的标签,也就是说,在处理某个标签的时候,比如在 startElement方法中,所能够得到的信息就是标签的名字和属性,至于标签内部的嵌套结构,上层标签、下层标签以及其兄弟节点的名称等等与其结构相关的信息都是不得而知的。实际上就是把XML文件的结构信息丢掉了,如果需要得到这些信息的话,只能你自己在程序里进行处理了。所以相对DOM而言,SAX处理XML文档没有DOM方便,SAX处理的过程相对DOM而言也比较复杂。

为了说明sax解析xml的过程,此xml文件非标准规范的xml文档

books.xml的内容:

<?xml version='1.0' encoding='UTF-8'?>
<books>---books---
<book id="12">---book---
<name>thinking in java</name>---/name---
<price>85.5</price>---/price---
</book>---/book---
<book id="15">---book2---
<name>Spring in Action</name>---/name2---
<price>39.0</price>---/price---
</book>---/book2---
</books>---/books---

Java Sax解析是按照xml文件的顺序一步一步的来解析,在解析xml文件之前,我们要先了解xml文件的节点的种类,一种是ElementNode,一种是TextNode。如上面的这段books.xml。

其中,像<books>、<book>这种节点就属于ElementNode,而thinking in java、85.5这种就属于TextNode。还有一种特殊情况:---book--- 、---/name--- 、---/book---等都是TextNode,sax是基于事件解析xml的那么遇到这些文本节点的时候也会触发characters( )方法。(这也是books.xml写成不规范的原因,方便去理解sax解析的过程)。

既然sax是基于事件解析xml文档,那么哪些xml包含什么呢?

    <?xml version='1.0' encoding='UTF-8'?>  这个代表什么?不就是文档开始部分吗, sax就把它作为一个事件 startDocument() ,你重写这个方法就行了。

    <book id="12">  遇到元素节点的开始标签要不要作为一个事件? 当然!    startElement()  ,现在你会想到结束标签呢?同样 endElement() .....

那么接下来看看这段文字:

startDocument()

当遇到文档的开头的时候,调用这个方法,可以在其中做一些预处理的工作。

endDocument()

和上面的方法相对应,当文档结束的时候,调用这个方法,可以在其中做一些善后的工作。

startElement(String uri, String localName, String qName, Attributes atts)

当读到一个开始标签的时候,会触发这个方法。uri是命名空间(通过xmlns声明),localName是不带命名空间前缀的标签名,qName是带命名空间前缀的标签名。通过atts可以得到所有的属性名和相应的值。注意,如果没有指定Namespace,则qName可能为空,当然不同的SAX实现会有所不同,比如在Android中qName为空,而J2SE中localName为空,所以想要总是得到标签名,就需要检查这两个参数的值了。

endElement(String uri, String localName, String name)

这个方法和上面的方法相对应,在遇到结束标签的时候,调用这个方法。

characters(char[] ch, int start, int length)

这个方法用来处理在XML文件中读到的内容,第一个参数为文件的字符串内容,后面两个参数是读到的字符串在这个数组中的起始位置和长度,使用new String(ch,start,length)就可以获取内容。

我们只要继承DefaultHandler 重写我们需要的方法就行了。

SaxParseHandler.java  让sax解析器遇到相应事件时做相应的事情:

package com.aib.sax;

import java.util.ArrayList;
import java.util.List; import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler; public class SaxParseHandler extends DefaultHandler { /***
* list 组装数据 Book 封装book信息的javabean currentTag
* 当前标签,在characters()方法中判断是否为我们处理文本信息所在的标签位置
*/
private List<Book> list;
private Book book; @Override
public String toString() {
return "SaxParseHandler [list=" + list + "]";
} public List<Book> getList() {
return list;
} private String currentTag; @Override
public void characters(char[] ch, int start, int length)
throws SAXException {
// 输出ch,用于测试...
System.out.println("characters方法中currentTag=" + currentTag);
System.out.println("characters方法中解析到的文本为:" + new String(ch,start,length)+"---结束标记---"); if (currentTag != null ) {
if (currentTag.equals("name")) {
book.setBookName(new String(ch, start, length));
} else if (currentTag.equals("price")) {
book.setPrice(new String(ch, start, length));
}
} } @Override
public void startDocument() throws SAXException {
// 根据这个信息: <?xml version="1.0" encoding="UTF-8"?> 识别
System.out.println("startDocument方法中currentTag=" + currentTag);
// 已经开始读取文档,所以初始化list用于保存接下来读取的book信息。注意:此方法调用一次!
list = new ArrayList<Book>();
} @Override
public void endDocument() throws SAXException {
System.out.println("endDocument方法中currentTag=" + currentTag);
} @Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException { // 将正在解析元素节点的名称赋值给currentTag,在characters()中可以准确处理文本信息
this.currentTag = qName;
// 打印参数信息,用于测试..
System.out.println("startElement方法中currentTag=" + currentTag); if ("book".equals(qName)) {
book = new Book();
book.setId(attributes.getValue("id")); } else if ("其他你要处理的elementNode".equals(qName)) { // 本程序为了方便演示,xml文档只提取book节点的信息
} } @Override
public void endElement(String uri, String localName, String qName)
throws SAXException { if ("book".equals(qName)) {
list.add(book);
book = null; } else if ("其他节点".equals(qName)) {
//
}
System.out.println("endElement方法中currentTag=" + currentTag);
this.currentTag = null; }
}

再写一个测试类测试我们的代码:

package com.aib.sax;

import java.io.InputStream;
import java.util.List; import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory; public class Test { public static void main(String[] args) throws Exception {
// 通过工厂方式获取 SAXParser
SAXParser parse = SAXParserFactory.newInstance().newSAXParser(); // sax是基于事件解析xml ,需要程序员编写如何去处理xml文件 。
SaxParseHandler saxHandler = new SaxParseHandler();
// 通过类加载器加载xml文件,返回一个InputStream,实际应用中可以通过不同的方式加载,比如解析网络的xml文件
InputStream is = Test.class.getClassLoader().getResourceAsStream(
"books.xml");
parse.parse(is, saxHandler);
List<Book> books = saxHandler.getList();
System.out.println("\n\n");
for (Book book : books) {
System.out.println("id=" + book.getId() + "\tname="
+ book.getBookName() + "\tprice="+book.getPrice());
} } }

这是输出的结果:

 startDocument方法中currentTag=null
startElement方法中currentTag=books
characters方法中currentTag=books
characters方法中解析到的文本为:---book---
---结束标记---
startElement方法中currentTag=book
characters方法中currentTag=book
characters方法中解析到的文本为:---book---
---结束标记---
startElement方法中currentTag=name
characters方法中currentTag=name
characters方法中解析到的文本为:thinking in java---结束标记---
endElement方法中currentTag=name
characters方法中currentTag=null
characters方法中解析到的文本为:---/name---
---结束标记---
startElement方法中currentTag=price
characters方法中currentTag=price
characters方法中解析到的文本为:85.5---结束标记---
endElement方法中currentTag=price
characters方法中currentTag=null
characters方法中解析到的文本为:---/price---
---结束标记---
endElement方法中currentTag=null
characters方法中currentTag=null
characters方法中解析到的文本为:---/book---
---结束标记---
startElement方法中currentTag=book
characters方法中currentTag=book
characters方法中解析到的文本为:---book2---
---结束标记---
startElement方法中currentTag=name
characters方法中currentTag=name
characters方法中解析到的文本为:Spring in Action---结束标记---
endElement方法中currentTag=name
characters方法中currentTag=null
characters方法中解析到的文本为:---/name2---
---结束标记---
startElement方法中currentTag=price
characters方法中currentTag=price
characters方法中解析到的文本为:39.0---结束标记---
endElement方法中currentTag=price
characters方法中currentTag=null
characters方法中解析到的文本为:---/price---
---结束标记---
endElement方法中currentTag=null
characters方法中currentTag=null
characters方法中解析到的文本为:---/book2---
---结束标记---
endElement方法中currentTag=null
endDocument方法中currentTag=null id=12 name=thinking in java price=85.5
id=15 name=Spring in Action price=39.0

很清楚的看到

---books---

---/name---

---/book2---等这些内容会调用characters()方法, 说明这也是一个文本节点。

现在我们把books.xml文档修改成如下:

 <?xml version='1.0' encoding='UTF-8'?>
<books><book id="12"><name>thinking in java</name><price>85.5</price></book><book id="15"><name>Spring in Action</name><price>39.0</price></book></books>

程序运行的结果就变成了:

 startDocument方法中currentTag=null
startElement方法中currentTag=books
startElement方法中currentTag=book
startElement方法中currentTag=name
characters方法中currentTag=name
characters方法中解析到的文本为:thinking in java---结束标记---
endElement方法中currentTag=name
startElement方法中currentTag=price
characters方法中currentTag=price
characters方法中解析到的文本为:85.5---结束标记---
endElement方法中currentTag=price
endElement方法中currentTag=null
startElement方法中currentTag=book
startElement方法中currentTag=name
characters方法中currentTag=name
characters方法中解析到的文本为:Spring in Action---结束标记---
endElement方法中currentTag=name
startElement方法中currentTag=price
characters方法中currentTag=price
characters方法中解析到的文本为:39.0---结束标记---
endElement方法中currentTag=price
endElement方法中currentTag=null
endElement方法中currentTag=null
endDocument方法中currentTag=null id=12 name=thinking in java price=85.5
id=15 name=Spring in Action price=39.0

我们可以看到<books><book id="12">  两个标签之间没有内容(空格、换行符也算内容)时不会触发characters()方法。

特别注意:

  1. public void endElement(String uri, String localName, String qName)
  2. throws SAXException {
  3. if("book".equals(qName)){
  4. books.add(book);
  5. book = null;
  6. }
  7. currentTag= null;/**当解析结束时置为空。这里很重要,例如遇到</book>---/book--- <book id="15">,会调用这个方法
  8. ,如果这里不把currentTag置为null,根据startElement(....)方法,currentTag的值还是price,因为两个标签之间有内容(---/book---),会执行characters(char[] ch, int start, int length)这个方法,而characters(....)方
  9. 法判断currentTag!=null,会执行if判断的代码,这样就会把空值赋值给book(因为执行了book=null,此时会有空指针异常),这不是我们想要的。*/
  10. }

SAX解析xml浅析的更多相关文章

  1. Android之SAX解析XML

    一.SAX解析方法介绍 SAX(Simple API for XML)是一个解析速度快并且占用内存少的XML解析器,非常适合用于Android等移动设备. SAX解析器是一种基于事件的解析器,事件驱动 ...

  2. Android 使用pull,sax解析xml

    pull解析xml文件 1.获得XmlpullParser类的引用 这里有两种方法 //解析器工厂 XmlPullParserFactory factory=XmlPullParserFactory. ...

  3. JAVA使用SAX解析XML文件

    在我的另一篇文章(http://www.cnblogs.com/anivia/p/5849712.html)中,通过一个例子介绍了使用DOM来解析XML文件,那么本篇文章通过相同的XML文件介绍如何使 ...

  4. DOM&SAX解析XML

    在上一篇随笔中分析了xml以及它的两种验证方式.我们有了xml,但是里面的内容要怎么才能得到呢?如果得不到的话,那么还是没用的,解析xml的方式主要有DOM跟SAX,其中DOM是W3C官方的解析方式, ...

  5. cocos2d-x 3.0 使用Sax解析xml文件(中国显示器问题解决)

    今天是个好日子.我以为事情可以变得,明天是个好日子.打开门儿春风... 恩,听着歌写文档生活就是这么享受. 今天曾经的邻居大神突然在qq上赞了我一下,这让我异常激动啊.. 这还要从前前前几天说起,那会 ...

  6. JavaWeb学习日记----SAX解析XML

    1.SAX解析XML文档的方式: 与DOM方式解析不同,DOM方式解析是根据XML的层级结构在内存中分配一个树形结构,把xml的标签,属性和文本都封装成对象.优点是可以很方便实现增删改操作.缺点是,如 ...

  7. 用SAX解析xml文件,java

    (此文为(https://www.imooc.com/video/4482)之随笔) 1.用SAX解析xml文件大致分为三步 写了一个XML文件作为例子 (1)main方法代码如下: import j ...

  8. Python:使用基于事件驱动的SAX解析XML

    SAX的特点: 是基于事件的 API 在一个比 DOM 低的级别上操作 为您提供比 DOM 更多的控制 几乎总是比 DOM 更有效率 但不幸的是,需要比 DOM 更多的工作 基于对象和基于事件的接口 ...

  9. Android SAX解析XML

    本篇讲解一下SAX解析XML这种方式,首先来看一下它的基本介绍: SAX是一种以事件驱动的XML API,由它定义的事件流可以指定从解析器传到专门的处理程序的代码的XML结构,简单的讲,它是种解析速度 ...

随机推荐

  1. sql事务,在sql2000里判断执行是否成功用@@ERROR 判断

    原文:sql事务,在sql2000里判断执行是否成功用@@ERROR 判断 贴个sql事务,在sql2000里判断执行是否成功用@@ERROR 判断 这个东西多少还是有点问题,sql2005了可以用t ...

  2. mysql_MYSQL远程登录权限设置

    Mysql默认关闭远程登录权限,如下操作允许用户在任意地点登录: 1. 进入mysql,GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY ...

  3. 表达式树 Expression

    转载泛型方法动态生成表达式树 Expression public string GetGridJSON(TraderInfo model) { IQueryable<TraderInfo> ...

  4. 用Iconv应对NodeJs对称加密技术在汉字编码与NoSQL的一些坑洞

    ·起因 汉字编码技术在实际应用中总是会存在这样或者那样的问题,尤其是在一些热门NoSQL方面多少会遇到挑战.比方说Cassandra字符集还不直接支持GB2312,要想存储写汉字那可真是麻烦.当然这还 ...

  5. 2014阿里巴巴研发project师暑期实习生面试经验

    2014阿里巴巴研发project师暑期实习生面试经验 作者:林子 Blog:  http://blog.csdn.net/u013011841 时间:2014年8月 出处:http://blog.c ...

  6. Lua的多任务机制——协程(coroutine)

    并发是现实世界的本质特征,而聪明的计算机科学家用来模拟并发的技术手段便是多任务机制.大致上有这么两种多任务技术,一种是抢占式多任务(preemptive multitasking),它让操作系统来决定 ...

  7. Hibernat之关系的处理一对多/多对一

    第一步:编写两个pojo,比如一个学生表一个班级表  这里使用注解. 需要 班级表: package com.qcf.pox; import java.util.HashSet; import jav ...

  8. Cocos2d-x3.0 TestCPP文件夹的注意事项

    1.不多说了,重力加速度. 2.ActionMangerTest:此Test它是由导演来展示,以获得集体诉讼经理ActionManager类别,操作控制节点. ①CrashTest:破坏demo,毁. ...

  9. Apache无法启动解决 the requested operation has failed

    Apache不能启动解决办法 原因一:80端口占用例如IIS,另外就是迅雷. 原因二:软件冲突装了某些软件会使apache无法启动如Dr.com 你打开网络连接->TcpIp属性->高级- ...

  10. ActiveReports 9实战教程(2): 准备数据源(设计时、运行时)

    原文:ActiveReports 9实战教程(2): 准备数据源(设计时.运行时) 在上讲中<ActiveReports 9实战教程(1): 手把手搭建环境Visual Studio 2013 ...