XML SAX解析是基于事件驱动的流式XML解析技术,它在解析过程中不会将整个XML文档加载到内存,而是逐行读取文档内容,当遇到特定的XML结构时触发对应的事件回调,开发者可以通过自定义事件处理器来捕获这些事件,完成对XML数据的提取和处理。

SAX解析的核心原理
SAX解析的核心工作机制是事件驱动,解析器在读取XML文档的过程中,会依次触发不同的事件,比如文档开始、元素开始、元素内容、元素结束、文档结束等。开发者需要继承SAX提供的默认处理器类,重写对应的事件回调方法,就可以在方法中编写自己的数据处理逻辑。
和DOM解析相比,SAX解析的优势非常明显:内存占用极低,因为它不需要存储整个文档的树形结构,只需要在回调中处理当前遇到的节点信息;解析速度更快,因为是流式处理,不需要等待整个文档加载完成。不过它的缺点也很突出:只能顺序读取XML内容,无法随机访问某个节点,也不支持修改XML文档。
Java中使用SAX解析XML的步骤
在Java中,SAX解析的相关类都位于javax.xml.parsers和org.xml.sax包下,完整的解析流程可以分为以下几步:
- 创建SAX解析器工厂实例
SAXParserFactory - 通过工厂实例获取SAX解析器
SAXParser - 自定义事件处理器,继承
DefaultHandler类,重写需要的回调方法 - 调用解析器的
parse方法,传入XML文件路径和自定义处理器,启动解析
自定义DefaultHandler处理器
DefaultHandler是SAX提供的默认事件处理器类,里面已经实现了所有事件回调的空方法,我们只需要重写需要的方法即可,常用的方法有以下这些:
| 方法名 | 触发时机 |
|---|---|
startDocument() | XML文档开始解析时触发 |
endDocument() | XML文档解析完成时触发 |
startElement(String uri, String localName, String qName, Attributes attributes) | 遇到元素开始标签时触发,qName是元素的限定名,attributes是元素的属性集合 |
endElement(String uri, String localName, String qName) | 遇到元素结束标签时触发 |
characters(char[] ch, int start, int length) | 遇到元素内容时触发,ch是内容字符数组,start是起始位置,length是内容长度 |
完整代码示例
假设我们有一个存储用户信息的XML文件users.xml,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<users>
<user id="1">
<name>张三</name>
<age>25</age>
<email>zhangsan@ipipp.com</email>
</user>
<user id="2">
<name>李四</name>
<age>28</age>
<email>lisi@ipipp.com</email>
</user>
</users>
下面我们自定义一个处理器来解析这个XML文件,提取所有用户的信息:
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.File;
// 自定义SAX事件处理器
class UserSAXHandler extends DefaultHandler {
// 临时存储当前解析的元素名
private String currentElement;
// 临时存储用户ID
private String userId;
// 临时存储用户名
private String userName;
// 临时存储用户年龄
private String userAge;
// 临时存储用户邮箱
private String userEmail;
// 文档开始解析时触发
@Override
public void startDocument() throws SAXException {
System.out.println("开始解析XML文档");
}
// 遇到元素开始标签时触发
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
currentElement = qName;
// 如果是user元素,获取id属性
if ("user".equals(qName)) {
userId = attributes.getValue("id");
}
}
// 遇到元素内容时触发
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
String content = new String(ch, start, length).trim();
// 忽略空白内容
if (content.length() == 0) {
return;
}
// 根据当前元素名存储对应内容
if ("name".equals(currentElement)) {
userName = content;
} else if ("age".equals(currentElement)) {
userAge = content;
} else if ("email".equals(currentElement)) {
userEmail = content;
}
}
// 遇到元素结束标签时触发
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
// 如果是user元素结束,说明一个用户的信息解析完成,输出结果
if ("user".equals(qName)) {
System.out.println("用户ID:" + userId);
System.out.println("用户名:" + userName);
System.out.println("年龄:" + userAge);
System.out.println("邮箱:" + userEmail);
System.out.println("-------------------");
// 重置临时变量
userId = null;
userName = null;
userAge = null;
userEmail = null;
}
currentElement = null;
}
// 文档解析完成时触发
@Override
public void endDocument() throws SAXException {
System.out.println("XML文档解析完成");
}
}
public class SAXParseDemo {
public static void main(String[] args) {
try {
// 创建SAX解析器工厂
SAXParserFactory factory = SAXParserFactory.newInstance();
// 获取SAX解析器
SAXParser parser = factory.newSAXParser();
// 创建自定义处理器实例
UserSAXHandler handler = new UserSAXHandler();
// 启动解析,传入XML文件路径和处理器
parser.parse(new File("users.xml"), handler);
} catch (Exception e) {
e.printStackTrace();
}
}
}
注意事项
在使用SAX解析的时候,需要注意characters方法的触发规则:它可能会在解析一个元素内容的时候多次触发,比如元素内容中包含换行或者特殊字符的时候,所以如果需要完整获取元素内容,最好是在characters方法中拼接字符,或者在endElement方法中统一处理拼接后的内容。
另外,如果XML文档中包含命名空间,startElement方法中的uri和localName参数会包含对应的命名空间信息,而qName是带前缀的元素名,需要根据实际情况选择使用的参数。
适用场景
SAX解析非常适合以下场景:处理体积非常大的XML文件,比如几个G的XML数据文件,DOM解析会直接内存溢出,而SAX解析可以轻松处理;只需要读取XML中的数据,不需要修改或者随机访问节点;对内存占用有严格要求的服务端应用,比如安卓客户端解析服务器返回的XML数据等。
XMLSAX解析事件驱动DefaultHandler字符流修改时间:2026-06-19 18:00:29