乐途乐途
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
  • 学习路径
  • XML 基础语法

    • XML 概述
    • XML 文档结构
    • XML 元素
    • XML 属性
    • XML 语法规则
    • XML 命名空间
    • XML 注释与处理指令
  • DTD 与文档验证

    • DTD 概述
    • DTD 元素声明
    • DTD 属性声明
    • DTD 实体声明
    • DTD 元素与属性对比
    • DTD 完整示例
  • XML Schema 定义

    • XML Schema 概述
    • XSD 简单类型
    • XSD 复杂类型
    • XSD 命名空间与引用
  • XPath 节点定位

    • XPath 概述
    • XPath 路径表达式
    • XPath 谓词与函数
  • XSLT 转换

    • XSLT 概述
    • XSLT 模板与匹配
    • XSLT 控制结构
    • XSLT 输出控制
  • XML 解析技术

    • XML 解析概述
    • DOM 解析
    • SAX 解析
    • StAX 解析
    • XML 与 Java — JAXP
  • XML 在 Java 中的应用

    • Spring XML 配置
    • MyBatis XML 映射
    • pom.xml 与 Maven
    • web.xml 配置详解
  • 现代数据格式对比

    • XML 与 JSON 对比
    • XML 与 YAML 对比
  • XML 显示与浏览器集成

    • XML 在浏览器中的显示
    • XMLHttpRequest 与 AJAX
    • 服务器端 XML 处理
  • XML 进阶查询与链接

    • XQuery 查询语言
    • XLink 超链接
    • XML 验证工具使用
  • XML Web 服务(选读)

    • XML Web 服务概述
    • SOAP 协议详解
    • WSDL 服务描述
    • RSS 内容聚合
    • RDF 资源描述框架

StAX 解析

本章定位 :掌握 StAX(Streaming API for XML)的拉式解析模型——XMLStreamReader 游标 API 和 XMLEventReader 迭代器 API,以及与 SAX 的差异。

定义与作用

StAX (Streaming API for XML)是 Java 6 引入的流式解析 API。与 SAX 的"推式"不同,StAX 是"拉式"——应用程序主动调用 next() 方法获取下一个事件。

这带来一个关键优势: 应用程序控制解析节奏 。可以随时停止、跳过不需要的部分、甚至在找到第一个匹配项后立即结束。

StAX 提供两种 API 风格:

  • 游标 API (XMLStreamReader):更快,更像指针遍历
  • 迭代器 API (XMLEventReader):更灵活,事件可缓存和回放

核心原理:SAX(推) vs StAX(拉)

图解释 :SAX 像电话推销(被动接听),解析器不停推送事件直到文档结束。StAX 像自助取餐(主动拿取),应用程序自己决定什么时候去拿下一个事件。

语法/结构要点

游标 API 关键方法

方法返回说明
next()int(事件类型常量)前进到下一个事件
getEventType()int当前事件类型
getLocalName()String当前元素/属性的本地名
getAttributeValue(index)String按索引取属性值
getElementText()String获取纯文本元素内容
hasNext()boolean是否还有事件

事件类型常量

常量对应 XML 结构
START_ELEMENT<tag>
END_ELEMENT</tag>
CHARACTERS文本内容
START_DOCUMENT文档开头
END_DOCUMENT文档结尾

完整示例:黄俪用 StAX 找到即停

场景说明

飞翔科技的测试 黄俪 有一个 50MB 的测试报告 XML,只想找"第一个 status='fail' 的用例"后立即停止。

游标 API 实现

import javax.xml.stream.*;
import java.io.FileInputStream;

public class FirstFailureFinder {
    public static void main(String[] args) throws Exception {
        XMLInputFactory factory = XMLInputFactory.newInstance();
        XMLStreamReader reader = factory.createXMLStreamReader(
            new FileInputStream("test_report.xml"));

        String currentElement = "";
        boolean found = false;

        while (reader.hasNext()) {
            int event = reader.next();

            if (event == XMLStreamReader.START_ELEMENT) {
                currentElement = reader.getLocalName();

                if ("case".equals(currentElement)) {
                    String status = reader.getAttributeValue(null, "status");
                    if ("fail".equals(status)) {
                        String id = reader.getAttributeValue(null, "id");
                        String name = reader.getElementText();
                        System.out.println("第一个失败用例: ["
                            + id + "] " + name);
                        found = true;
                        break;  // 找到即停!
                    }
                }
            }
        }

        if (!found) {
            System.out.println("所有用例均通过!");
        }

        reader.close();
    }
}

对比 SAX 实现

// SAX 做同样的事:必须解析整个文件(或手动抛异常)
class FindFirstSAX extends DefaultHandler {
    boolean found = false;
    public void startElement(String uri, String local,
                             String qName, Attributes atts) {
        if (found) return;  // 仍会触发后续回调
        if ("case".equals(qName) && "fail".equals(atts.getValue("status"))) {
            System.out.println("第一个失败用例: ["
                + atts.getValue("id") + "]");
            found = true;
            // SAX 没有优雅的"停止"机制……
        }
    }
}

操作结果

StAX 版本在找到第一个失败用例后立即 break 退出循环,剩下的 95% 文档完全不解析。而 SAX 版本即使用 found 标志跳过了处理逻辑,解析器仍然会继续扫描整个文件。

易错场景

错误一:getElementText 后当前位置变了

if ("title".equals(reader.getLocalName())) {
    String text = reader.getElementText();  // 消费了文本 + END_ELEMENT
    // 此时 reader 已经指向 title 之后的下一个事件
    // 不要再调用 getLocalName() 期望它是 "title"
}

getElementText() 会持续读取直到 END_ELEMENT,调用后当前位置已前进。

错误二:忘记检查事件类型

while (reader.hasNext()) {
    reader.next();
    String name = reader.getLocalName();
    // ❌ 如果是 CHARACTERS 事件,getLocalName() 抛异常
}

每次 next() 后应先检查 getEventType(),确认是 START_ELEMENT 再调用 getLocalName()。

面试考点

考点参考答案要点
StAX 的"拉式"模型与 SAX 的"推式"模型有何区别?SAX 解析器主动推送事件(被动接收),无法暂停;StAX 应用主动拉取事件(调用 next()),可随时停止、跳过、控制节奏
StAX 的两种 API 风格及选择?游标 API(XMLStreamReader)更快更省内存;迭代器 API(XMLEventReader)可缓存事件、支持回放。性能优先用游标,需要灵活性用迭代器
何时用 StAX 而非 SAX?需要按条件中途停止时;需要跳过大部分文档时;需要更好的代码可读性和可维护性(不用写复杂的回调状态机)
上一页
SAX 解析
下一页
XML 与 Java — JAXP