在Java生态中,PULL解析是一种基于事件驱动的XML解析方式,它的核心逻辑就是通过循环不断获取解析事件,再根据不同的事件类型处理对应的XML内容,整个过程不需要一次性把整个XML文件加载到内存中,适合处理大体积的XML数据。

PULL解析的核心事件类型
要实现PULL解析的循环过程,首先需要了解XmlPullParser提供的几种核心事件类型,循环的判断和处理都围绕这些事件展开:
- START_DOCUMENT:XML文档开始解析的事件,只会触发一次
- START_TAG:遇到XML开始标签时触发的事件
- TEXT:解析到标签内的文本内容时触发的事件
- END_TAG:遇到XML结束标签时触发的事件
- END_DOCUMENT:XML文档解析完成的事件,循环到这里就会结束
循环过程的基本逻辑
PULL解析的循环通常以XmlPullParser.next()方法为核心,每次调用这个方法就会获取下一个解析事件,然后把返回的事件类型和上述核心事件类型做对比,执行对应的处理逻辑,直到事件类型为END_DOCUMENT时退出循环。
完整示例代码
下面是一个解析简单用户列表XML的示例代码,XML内容包含多个user节点,每个节点有id、name、age三个子节点,代码完整展示了循环过程的实现:
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
public class PullXmlParseDemo {
// 待解析的XML字符串
private static final String XML_DATA = "<users>" +
"<user>" +
"<id>1</id>" +
"<name>张三</name>" +
"<age>25</age>" +
"</user>" +
"<user>" +
"<id>2</id>" +
"<name>李四</name>" +
"<age>28</age>" +
"</user>" +
"</users>";
static class User {
private int id;
private String name;
private int age;
// getter和setter方法
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
@Override
public String toString() {
return "User{id=" + id + ", name='" + name + "', age=" + age + "}";
}
}
public static List<User> parseXmlWithPull(String xmlData) throws Exception {
List<User> userList = new ArrayList<>();
User currentUser = null;
String currentTagName = null;
// 创建XmlPullParser工厂实例
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
// 获取XmlPullParser实例
XmlPullParser parser = factory.newPullParser();
// 设置待解析的输入源
parser.setInput(new StringReader(xmlData));
// 获取第一个解析事件
int eventType = parser.getEventType();
// 循环解析,直到文档结束
while (eventType != XmlPullParser.END_DOCUMENT) {
switch (eventType) {
case XmlPullParser.START_TAG:
// 遇到开始标签时的处理
currentTagName = parser.getName();
if ("user".equals(currentTagName)) {
// 遇到user开始标签,创建新的User对象
currentUser = new User();
}
break;
case XmlPullParser.TEXT:
// 遇到文本内容时的处理,把内容赋值给当前标签对应的属性
if (currentUser != null && currentTagName != null) {
String text = parser.getText().trim();
if (!text.isEmpty()) {
if ("id".equals(currentTagName)) {
currentUser.setId(Integer.parseInt(text));
} else if ("name".equals(currentTagName)) {
currentUser.setName(text);
} else if ("age".equals(currentTagName)) {
currentUser.setAge(Integer.parseInt(text));
}
}
}
break;
case XmlPullParser.END_TAG:
// 遇到结束标签时的处理
if ("user".equals(parser.getName())) {
// user标签结束,把当前User对象加入列表
userList.add(currentUser);
currentUser = null;
}
currentTagName = null;
break;
default:
break;
}
// 获取下一个解析事件,继续循环
eventType = parser.next();
}
return userList;
}
public static void main(String[] args) {
try {
List<User> users = parseXmlWithPull(XML_DATA);
for (User user : users) {
System.out.println(user);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
循环过程的注意事项
在实际编写PULL解析的循环代码时,需要注意几个问题:
- 每次处理完TEXT事件后,最好把当前标签名重置,避免后续空文本错误赋值
- 解析文本内容时要先判断文本是否为空,避免空指针或者格式转换异常
- 如果XML结构比较复杂,需要做好标签的层级判断,避免不同层级的同名标签处理错误
- 循环退出条件必须明确是END_DOCUMENT事件,不要遗漏这个判断导致死循环
如果解析的是本地XML文件或者网络请求的XML数据,只需要把parser.setInput的输入源换成对应的文件流或者网络流即可,循环逻辑不需要修改。