SAX 解析:基于事件驱动的 XML 解析方式
SAX(Simple API for XML)是一种基于事件驱动的 XML 解析标准,与 DOM 解析的树形结构不同,SAX 无需将整个 XML 文档加载到内存,而是通过逐行扫描文档并触发事件的方式处理数据。这种特性使其在处理大型 XML 文档时具有显著的内存优势,成为 DOM 解析的重要补充。
SAX 解析的核心原理
SAX 解析的核心思想是 “流式处理 + 事件驱动”,其工作流程如下:
- 逐行扫描:SAX 解析器按顺序读取 XML 文档,无需一次性加载整个文档到内存。
- 事件触发:当解析到特定结构(如元素开始、文本内容、元素结束等)时,解析器触发对应的事件(如
startElement
、characters
、endElement
)。 - 事件处理:开发者通过自定义事件处理器(继承
DefaultHandler
),重写事件方法以响应特定事件,提取所需数据。 - 按需停止:解析过程中可随时终止(如找到目标数据后),无需解析剩余内容,节省资源。
SAX 解析的核心组件
SAX 解析主要依赖以下组件完成解析流程:
组件 | 作用 |
---|---|
SAXParserFactory |
SAX 解析器的工厂类,用于创建SAXParser 实例 |
SAXParser |
SAX 解析器的核心接口,负责读取 XML 并触发事件 |
DefaultHandler |
事件处理器的基类,实现了 SAX 的四大事件监听接口,提供空方法实现 |
事件监听接口 | 包括ContentHandler 、DTDHandler 、EntityResolver 、ErrorHandler ,定义了各类事件的回调方法 |
四大事件监听接口
SAX 通过四大接口定义不同类型的事件,DefaultHandler
实现了这些接口并提供空方法,开发者可按需重写:
(1)ContentHandler
(核心接口,处理文档内容)
定义与 XML 元素、文本相关的事件,是最常用的接口:
startDocument()
:文档解析开始时触发。endDocument()
:文档解析结束时触发。- startElement(String uri, String localName, String qName, Attributes atts):元素开始时触发(如
<book>
)。uri
:命名空间 URI(无则为空)。localName
:不带前缀的元素名。qName
:带前缀的元素名(如ns:book
)。atts
:元素的属性集合。
endElement(String uri, String localName, String qName)
:元素结束时触发(如</book>
)。characters(char[] ch, int start, int length)
:解析到文本内容时触发,ch
为字符数组,start
和length
指定文本范围。startPrefixMapping(String prefix, String uri)
:命名空间前缀映射开始时触发。endPrefixMapping(String prefix)
:命名空间前缀映射结束时触发。
(2)DTDHandler
(处理 DTD 相关事件)
notationDecl(String name, String publicId, String systemId)
:解析 DTD 符号声明时触发。unparsedEntityDecl(String name, String publicId, String systemId, String notationName)
:解析未解析实体时触发。
(3)EntityResolver
(处理实体解析)
resolveEntity(String publicId, String systemId)
:解析外部实体(如 DTD 文件)时触发,可自定义实体加载方式(如本地缓存)。
(4)ErrorHandler
(处理解析错误)
warning(SAXParseException e)
:非致命错误(如语法警告)。error(SAXParseException e)
:可恢复的错误(如未闭合标签)。fatalError(SAXParseException e)
:致命错误(如文档格式错误),会终止解析。
SAX 解析实战示例
以解析 MyBatis 的mapper.xml
为例,演示 SAX 解析的核心操作:
示例 XML 文档(UserMapper.xml
)
1 |
|
自定义事件处理器
1 | import org.xml.sax.Attributes; |
执行 SAX 解析
1 | import javax.xml.parsers.SAXParser; |
输出结果:
1 | 开始解析文档... |
SAX 解析的优缺点
优点
- 内存效率高:无需加载整个文档到内存,仅在解析时临时存储少量数据,适合处理 GB 级大型 XML。
- 解析速度快:流式处理无需构建树形结构,启动速度快,适合只需读取数据的场景。
- 可中断性:解析过程中可随时终止(如
throw new SAXException()
),避免无效解析。 - 低资源占用:对内存和硬件配置要求低,适合嵌入式设备或资源受限的环境。
缺点
- 无法随机访问:流式处理只能单向前进,无法回溯已解析的节点,不支持随机查询。
- 状态管理复杂:需要手动维护节点间的层级关系(如嵌套元素),代码复杂度高于 DOM。
- 不支持写入:SAX 仅用于读取 XML,无法修改或生成 XML 文档。
- 事件驱动门槛:需理解事件触发机制,初学者可能难以掌握。
DOM 与 SAX 的对比与选择
维度 | DOM 解析 | SAX 解析 |
---|---|---|
核心思想 | 树形结构,加载整个文档到内存 | 事件驱动,流式处理,不存储文档结构 |
内存占用 | 高(与文档大小成正比) | 低(恒定,与文档大小无关) |
速度 | 慢(需构建完整树) | 快(逐行解析) |
随机访问 | 支持(可任意跳转节点) | 不支持(只能单向前进) |
读写支持 | 支持读取和修改 | 仅支持读取 |
适用场景 | 中小型文档、需修改或随机访问 | 大型文档、仅需读取数据、资源受限环境 |
选择建议:
- 中小型 XML、需修改结构或随机访问 → 选 DOM。
- 大型 XML、仅需读取数据、内存有限 → 选 SAX。
v1.3.10