Android開發裏,解析xml是很常見的場景了,其中SAX解析與PULL解析是兩種比較常用的解析方式。
PULL方式解析xml的文章可點這裏:Android XML解析之PULL解析 簡單示例講解
除此之外還有DOM解析,這個不太常用,因爲它需一次性把xml文件加載到內存裏,如果是大文件的話,很佔內存,影響性能。
而SAX(Simple API for XML)解析是事件驅動的流式解析方式,並不是把xml全部加載到內存,而是一邊讀取一邊解析,不可暫停或者倒退,直到結束。在解析過程中,會判斷當前的節點及內容,而觸發不同的方法被調用。我們需要重寫這些回調方法,實現自己的處理。
SAX解析的優點:佔用內存少,速度快,如果對性能要求比較高,推薦使用。
缺點:不像DOM解析一樣將文檔長期駐留在內存中,數據不是持久的。如果事件過後沒有保存數據,數據就會丟失。
本篇文章,會用一個簡單的例子示例下SAX解析的用法。
一、先準備一個簡單的xml文件。
路徑:res/raw/person_data.xml:
<?xml version="1.0" encoding="UTF-8"?>
<data>
<person id="5">
<name>Jack</name>
<age>25</age>
</person>
<person id="20">
<name>Rose</name>
<age>80</age>
</person>
</data>
二、新建一個Person類。
我們的目標是把上面的xml數據(兩個person)轉換成一個Person列表。
Person類如下,和xml裏的屬性一一對應:
public class Person {
private int id;
private String name;
private int age;
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 "Person{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
下面先看看如何用SAX方式解析上面的xml:
三、定義解析處理器。
我們需要繼承自org.xml.sax.helpers.DefaultHandler類,實現一個解析處理器類。這個DefaultHandler類裏比較重要的方法是下面幾個,可以選擇性的重寫:
startDocument() —— 開始解析的時候被調用,可以在此方法裏做一些初始化的準備工作。
startElement(String uri, String localName, String qName, Attributes attributes) —— 此方法在每次解析到一個開始節點的時候被調用。其中uri表示元素的命名空間;localName表示元素的本地名稱(不帶前綴);qName表示元素的限定名(帶前綴);attrs表示元素的屬性集合。
characters(char[] ch, int start, int length) —— 接收字符數據的通知。改方法用來處理在XML文件中讀到的內容,第一個參數用來存放文件的內容,後面兩個參數是讀到的字符串在這個數組中的起始位置和長度。使用newSreing(ch,start,length)就可以獲取內容。這個方法被調用的次數最多,包括換行符也會被當做內容而傳進這個方法裏。
endElement(String uri, String localName, String qName) —— 在每次遇到結束節點的時候被調用。其中uri表示元素的命名空間;localName表示元素的本地名稱(不帶前綴);name表示元素的限定名(帶前綴)。
endDocument() —— 解析結束的時候被調用。
下面看代碼:
import android.util.Log;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import java.util.ArrayList;
import java.util.List;
public class MyContentHandler extends DefaultHandler {
private static final String TAG = "TAG";
private String currentNodeName; //當前的節點名稱
private List<Person> personList;
private Person person; // 當前正在解析的Person
// 這裏定義這個方法是爲了把解析結果提供出去
public List<Person> getPersonList() {
return personList;
}
@Override
public void startDocument() throws SAXException {
Log.d(TAG, "startDocument() called");
personList = new ArrayList<>();
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
Log.d(TAG, "startElement() called with: uri = [" + uri + "], localName = [" + localName + "], qName = [" + qName + "]");
// 記錄當前節點名稱
currentNodeName = localName;
// 每次開始節點爲“person”的話,就要開始解析一個新的person對象了
if (localName.equals("person")) {
person = new Person();
// 獲取person節點的id屬性
person.setId(Integer.parseInt(attributes.getValue("id")));
}
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
Log.d(TAG, "characters() called with: ch = [" + String.valueOf(ch) + "], start = [" + start + "], length = [" + length + "]");
if (currentNodeName != null) {
String data = new String(ch, start, length);
if (currentNodeName.equals("name")) {
person.setName(data);
} else if (currentNodeName.equals("age")) {
person.setAge(Integer.parseInt(data));
}
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
Log.d(TAG, "endElement() called with: uri = [" + uri + "], localName = [" + localName + "], qName = [" + qName + "]");
// 每次結束節點爲“person”的話,則一個person對象的解析結束了
if (localName.equals("person")) {
personList.add(person);
person = null;
}
// 把當前節點重置
currentNodeName = null;
}
@Override
public void endDocument() throws SAXException {
Log.d(TAG, "endDocument() called");
}
}
接下來,我們就可以使用這個解析處理器進行解析了。
四、使用解析處理器進行xml解析。
怎麼用呢?如下面的方法:
public static void saxParseXml(Context context) {
try {
SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
XMLReader reader = saxParser.getXMLReader();
// 這裏用到了我們自定義的解析執行器
MyContentHandler contentHandler = new MyContentHandler();
reader.setContentHandler(contentHandler);
// 讀取xml文件,執行解析
reader.parse(new InputSource(context.getResources().openRawResource(R.raw.person_data)));
// 得到解析結果
List<Person> personList = contentHandler.getPersonList();
Log.d("TAG", "parse result: " + personList);
} catch (SAXException e){
} catch (ParserConfigurationException e) {
} catch (IOException e) {
}
}
調用上面的方法即可解析res/raw/person_data.xml文件並得到Person類列表了。
爲了更直觀地查看解析的流程及解析類的幾個方法的參數,我都加上了log。
我是在某個按鈕的點擊事件裏調用這個方法的,運行後打印如下。可對照看到各個方法的執行順序。
D/TAG: startDocument() called
D/TAG: startElement() called with: uri = [], localName = [data], qName = [data]
characters() called with: ch = [
], start = [0], length = [1]
characters() called with: ch = [ ], start = [0], length = [4]
D/TAG: startElement() called with: uri = [], localName = [person], qName = [person]
characters() called with: ch = [
], start = [0], length = [1]
characters() called with: ch = [ ], start = [0], length = [8]
startElement() called with: uri = [], localName = [name], qName = [name]
characters() called with: ch = [Jack ], start = [0], length = [4]
D/TAG: endElement() called with: uri = [], localName = [name], qName = [name]
characters() called with: ch = [
ack ], start = [0], length = [1]
characters() called with: ch = [ ], start = [0], length = [8]
startElement() called with: uri = [], localName = [age], qName = [age]
characters() called with: ch = [25 ], start = [0], length = [2]
D/TAG: endElement() called with: uri = [], localName = [age], qName = [age]
characters() called with: ch = [
5 ], start = [0], length = [1]
characters() called with: ch = [ ], start = [0], length = [4]
endElement() called with: uri = [], localName = [person], qName = [person]
characters() called with: ch = [
], start = [0], length = [1]
characters() called with: ch = [ ], start = [0], length = [4]
startElement() called with: uri = [], localName = [person], qName = [person]
D/TAG: characters() called with: ch = [
], start = [0], length = [1]
characters() called with: ch = [ ], start = [0], length = [8]
startElement() called with: uri = [], localName = [name], qName = [name]
characters() called with: ch = [Rose ], start = [0], length = [4]
endElement() called with: uri = [], localName = [name], qName = [name]
characters() called with: ch = [
ose ], start = [0], length = [1]
characters() called with: ch = [ ], start = [0], length = [8]
startElement() called with: uri = [], localName = [age], qName = [age]
characters() called with: ch = [80 ], start = [0], length = [2]
endElement() called with: uri = [], localName = [age], qName = [age]
D/TAG: characters() called with: ch = [
0 ], start = [0], length = [1]
characters() called with: ch = [ ], start = [0], length = [4]
endElement() called with: uri = [], localName = [person], qName = [person]
characters() called with: ch = [
], start = [0], length = [1]
endElement() called with: uri = [], localName = [data], qName = [data]
D/TAG: endDocument() called
當然最後也得到了Person列表,打印如下:
parse result: [Person{id=5, name='Jack', age=25}, Person{id=20, name='Rose', age=80}]
這是PULL方式解析xml的文章鏈接,可以對照查看:
Android XML解析之PULL解析 簡單示例講解