12.02 数据解析-XML解析
XML 在 Android 里有两个身份:一是系统资源格式(layout、manifest、strings 全是 XML——但那些由系统解析,不需要你写解析代码),二是服务端接口格式(老接口、RSS、部分政企系统仍在用)。本节讲第二种场景的三种解析器与选型,并延伸到数据模型(DTO)的设计原则——这部分与格式无关,是解析工作的"上层建筑"。
# 一、三种 XML 解析器对比
| 解析器 | 模式 | 内存 | 速度 | 适用场景 |
|---|---|---|---|---|
| DOM(Document) | 树模型:整棵读进内存 | 高(文档大易 OOM) | 快(随机访问方便) | 小文档、需要反复随机访问节点 |
| SAX(Simple API) | 流式推送:边读边回调 | 最低 | 快 | 只读一遍的大文档 |
| Pull(XmlPullParser) | 流式拉取:主动 next() 推进 | 最低 | 快 | Android 推荐,可控性强 |
Push 与 Pull 的区别(面试常问):SAX 是"解析器推给你"——注册监听器,事件发生时被动回调,逻辑分散在回调里;Pull 是"你主动拉"——循环调用 next() 取事件,用普通循环和分支控制整个解析流程,可随时中断、跳过,代码更直观。Android 内置 Pull(org.xmlpull),也是系统资源解析采用的方案。
# 二、XmlPullParser 实战
以下面这段 RSS/接口数据为例:
<?xml version="1.0" encoding="utf-8"?>
<articles>
<article id="1">
<title>Handler 消息机制原理</title>
<author>JiangMing</author>
<tags>
<tag>Android</tag>
<tag>Handler</tag>
</tags>
</article>
<article id="2">
<title>数据存储</title>
<author>JiangMing</author>
<tags>
<tag>SQLite</tag>
</tags>
</article>
</articles>
# 1. 数据实体
public class Article {
public long id;
public String title;
public String author;
public List<String> tags = new ArrayList<>();
}
# 2. Pull 解析实现
public class ArticleXmlParser {
public static List<Article> parse(InputStream in) throws Exception {
List<Article> articles = new ArrayList<>();
XmlPullParser parser = Xml.newPullParser();
parser.setInput(in, "UTF-8");
int event = parser.getEventType(); // 第一个事件:START_DOCUMENT
Article current = null;
String currentTag = null;
while (event != XmlPullParser.END_DOCUMENT) { // 循环推进,直到文档结束
switch (event) {
case XmlPullParser.START_TAG:
switch (parser.getName()) {
case "article":
current = new Article();
// 读取属性:id="1"是article的attribute而非文本
current.id = Long.parseLong(parser.getAttributeValue(0));
break;
case "title":
currentTag = "title";
break;
case "author":
currentTag = "author";
break;
case "tag":
currentTag = "tag";
break;
default:
break;
}
break;
case XmlPullParser.TEXT:
// TEXT事件:当前标签内的文本(注意可能包含空白换行)
if (current != null && currentTag != null) {
String text = parser.getText().trim();
if (!text.isEmpty()) {
switch (currentTag) {
case "title":
current.title = text;
break;
case "author":
current.author = text;
break;
case "tag":
current.tags.add(text);
break;
default:
break;
}
}
}
break;
case XmlPullParser.END_TAG:
if ("article".equals(parser.getName())) {
articles.add(current); // 一个article闭合:收集
current = null;
}
currentTag = null;
break;
default:
break;
}
event = parser.next(); // 主动拉取下一个事件
}
return articles;
}
}
// 使用(配合11章网络请求)
ThreadPoolManager.execute(() -> {
Response response = OkHttpHolder.getClient()
.newCall(new Request.Builder().url("https://example.com/feed.xml").build())
.execute();
if (response.body() != null) {
List<Article> articles = ArticleXmlParser.parse(response.body().byteStream());
// 切主线程更新UI(10章范式)
}
});
Pull 解析的四个关键事件:START_DOCUMENT → 循环处理 START_TAG / TEXT / END_TAG → END_DOCUMENT。写法的固定套路:用 currentTag 记住"我正在处理哪个标签的文本",在 TEXT 事件里按它分发赋值。
# 3. DOM 解析参考(对比用)
// DOM:一次加载成树,适合小文档
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
Document doc = factory.newDocumentBuilder().parse(inputStream);
NodeList nodes = doc.getElementsByTagName("article");
for (int i = 0; i < nodes.getLength(); i++) {
Element article = (Element) nodes.item(i);
String id = article.getAttribute("id");
String title = article.getElementsByTagName("title").item(0).getTextContent();
}
# 三、数据模型(DTO)设计原则
解析质量一半取决于解析器,另一半取决于模型设计。这部分内容较多,已单独成篇:12.03 数据模型(DTO)设计——包含 DTO/VO/Mapper 三层架构、七条设计原则(可空语义、集合预初始化、映射统一、嵌套建模、UI 语义进 VO、不可变倾向、版本兼容)与端到端优秀实践案例、反例对照表。本节只留两条与 XML 直接相关的提醒:
- XML 解析结果同样落到 DTO/VO:上面的
Article类就是 DTO,Pull 解析循环本身兼任了 Mapper 的角色 - 属性与文本的建模差异:
<article id="1">的 id 建模为字段long id,<title>文本建模为String title——解析方式不同,但模型形态一致
# 四、常见问题与陷阱
- TEXT 事件被空白打断:标签内换行缩进会产生多个 TEXT 事件,
getText()后必须trim()+ 判空(上面代码已处理)。 - 属性 vs 文本:
<article id="1">的 id 是属性(getAttributeValue),<title>内容是文本事件,别混。 - DOM 解析大文档 OOM:树模型内存 ≈ 文档体积的数倍,RSS/大配置用 Pull。
- XML 实体转义:内容含
<&时 Pull 自动解码为<、&;反向生成 XML 时要转义。 - 命名空间:XML 带
xmlns前缀时用getNamespace()/getAttributeValue(ns, name),普通getName()可能拿到带前缀的名字。
# 结束
本节讲解了 XML 解析的三种解析器对比(DOM 树模型/SAX 推模型/Pull 拉模型——Android 推荐)、XmlPullParser 完整实战(四类事件 + currentTag 套路),并延伸到数据模型设计原则(DTO/VO 分层、包装类型、集合预初始化、版本兼容思维)。到这里数据解析整章完成,下一节处理 UI 耗资源大户——图片:12.04 图片加载。更多内容请参考:XML 解析 (opens new window)。