Pull 解析 (Pull Parsing) 是一種流式、基於事件的 XML 解析方式,與 SAX 類似但控制權在開發者手中:代碼主動調用 next() 推進解析器,根據當前事件類型 (START_TAG, TEXT, END_TAG...) 決定處理邏輯。
它是 Android 官方推薦、內置無需第三方庫 的解析器 (org.xmlpull.v1),輕量、高效、適合移動端及大文件流式處理。
<?xml version="1.0" encoding="UTF-8"?>
<students>
<student name="zhangsan">
<age>18</age>
<sex>男</sex>
</student>
<student name="lisi">
<age>22</age>
<sex>女</sex>
</student>
<student name="wangwu">
<age>20</age>
<sex>男</sex>
</student>
</students>
public class Student {
private String name;
private int age;
private String sex;
// 構造器、Getter/Setter、toString() 省略
}
public class PullParserDemo {
public static List<Student> parse(InputStream inputStream) throws XmlPullParserException, IOException {
// 1. 獲取解析器工廠與實例
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
// factory.setNamespaceAware(true); // 若 XML 有命名空間需開啟
XmlPullParser parser = factory.newPullParser();
// 2. 設置輸入源 (編碼需與 XML 聲明一致)
parser.setInput(inputStream, "UTF-8");
List<Student> students = null;
Student currentStudent = null;
// 3. 獲取初始事件類型
int eventType = parser.getEventType();
// 4. 循環處理直到文檔結束
while (eventType != XmlPullParser.END_DOCUMENT) {
switch (eventType) {
case XmlPullParser.START_TAG:
String tagName = parser.getName();
if ("students".equals(tagName)) {
students = new ArrayList<>(); // 初始化列表
} else if ("student".equals(tagName)) {
currentStudent = new Student();
// 獲取屬性值:命名空間為 null,屬性名為 "name"
currentStudent.setName(parser.getAttributeValue(null, "name"));
students.add(currentStudent); // 早加入列表,便於後續設置屬性
} else if ("age".equals(tagName)) {
// nextText() 會推進到 TEXT 事件並返回文本內容,再推進到 END_TAG
currentStudent.setAge(Integer.parseInt(parser.nextText()));
} else if ("sex".equals(tagName)) {
currentStudent.setSex(parser.nextText());
}
break;
case XmlPullParser.END_TAG:
// 結束標籤通常無需處理,除非需在元素結束時做特定邏輯
break;
case XmlPullParser.TEXT:
// 純文本節點,若未用 nextText() 可在這裡用 getText()
// 注意:若上層已用 nextText() 消費了 TEXT 事件,這裡不會再進入
break;
}
// 關鍵:推進到下一個事件
eventType = parser.next();
}
return students;
}
}
| 方法 | 說明 |
|---|---|
getEventType() | 獲取當前事件類型 (START_DOCUMENT, START_TAG, TEXT, END_TAG, END_DOCUMENT...) |
next() | 推進到下一個事件,返回新事件類型 |
nextText() | 便利方法:要求當前是 START_TAG,直接返回下一個 TEXT 內容,並自動推進到對應的 END_TAG。解析元素文本首選。 |
getName() | 獲取當前標籤名 (START_TAG/END_TAG 時有效) |
getAttributeValue(ns, name) | 獲取屬性值,ns 為命名空間 URI (無則 null) |
getText() | 獲取當前 TEXT 事件的文本內容 |
getDepth() | 獲取當前元素深度 (根元素深度 1),可用於判斷層級 |
| 維度 | DOM | SAX | Pull (XmlPullParser) |
|---|---|---|---|
| 記憶體佔用 | 高 (整棵樹加載內存) | 極低 (流式) | 極低 (流式) |
| 解析控制權 | 樹結構隨意遍歷 | 解析器推送事件 (回調) | 開發者拉取事件 (主動) |
| 代碼複雜度 | 低 (樹操作直觀) | 中 (狀態機管理) | 中低 (流程清晰,邏輯集中) |
| 修改 XML | 易 (內存樹修改後寫回) | 難 (只讀) | 難 (只讀) |
| Android 推薦度 | 小文件可用 | 不推薦 (需引包) | 首選 (系統內置) |
| 適用場景 | 配置文件、小數據、需隨機訪問/修改 | 大文件、只讀、內存受限、舊項目 | 大文件、只讀、Android、Kotlin/Java 通用、需精確控制流程 |
parser.setInput(stream, "UTF-8") 必須與 XML 頭聲明一致,否則中文亂碼。xmlns="...",需 factory.setNamespaceAware(true),且 getName() 返回帶前綴名,getAttributeValue(ns, name) 需傳入真實命名空間 URI。nextText() 前置條件:當前事件必須是 START_TAG,且元素必須包含文本內容 (空元素會拋異常),調用後指針移至 END_TAG。XmlPullParserException (格式錯誤) 和 IOException (IO錯誤)。try-with-resources 管理 InputStream。總結: Pull 解析以「主動拉取、流式處理、代碼邏輯集中」著稱,是 Android 及 Java 輕量級 XML 解析的首選。掌握
START_TAG判斷分發 +nextText()取值 +next()推進 這套核心節奏即可應對大多數場景。