XML解析(一),SAX解析XML
轉載自??XML解析(一),SAX解析XML
一、概述
?SAX,全稱Simple API for XML,是一種以事件驅動的XMl API,是XML解析的一種新的替代方法,解析XML常用的還有DOM解析,PULL解析(Android特有),SAX與DOM不同的是它邊掃描邊解析,自頂向下依次解析,由于邊掃描邊解析,所以它解析XML具有速度快,占用內(nèi)存少的優(yōu)點,對于Android等CPU資源寶貴的移動平臺來說是一個巨大的優(yōu)勢。
SAX的優(yōu)點:
- 解析速度快
- 占用內(nèi)存少
SAX的缺點:
- 無法知道當前解析標簽(節(jié)點)的上層標簽,及其嵌套結構,僅僅知道當前解析的標簽的名字和屬性,要知道其他信息需要程序猿自己編碼
- 只能讀取XML,無法修改XML
- 無法隨機訪問某個標簽(節(jié)點)
- SAX解析適用場合?
- 對于CPU資源寶貴的設備,如Android等移動設備
- 對于只需從xml讀取信息而無需修改xml
二、SAX解析的步驟
解析步驟很簡單,可分為以下四個步驟
知道了SAX解析的優(yōu)缺點和解析步驟,下面我們通過一個簡單的Demo學習一下SAX解析XML
三、SAX解析實戰(zhàn)
新建一個Android工程叫SaxParseXmlDemo,將sax.jar下載放到工程的lib下面并添加到構建路徑中,為了方便,我先將工程的目錄結構列一下:
1、新建一個xml文件叫users.xml
<?xml version="1.0" encoding="UTF-8"?> <users><user id="1"><name>畢向東</name><password>bxd123</password></user><user id="2"><name>韓順平</name><password>hsp123</password></user><user id="3"><name>馬士兵</name><password>msb123</password></user> </users>2、新建一個JavaBean
package com.example.saxparsexmldemo;public class User {private long id;private String name;private String password;public long getId() {return id;}public void setId(long id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;} }3、新建一個類XmlParseHandler.java,該類需要繼承DefaultHandler或者實現(xiàn)ContentHandler接口,這里我們通過繼承DefaultHandler(實現(xiàn)了ContentHandler接口)的方式,該類是SAX解析的核心所在,我們要重寫以下幾個我們關心的方法。
- startDocument():文檔解析開始時調(diào)用,該方法只會調(diào)用一次
startElement(String uri, String localName, String qName,?
Attributes attributes):標簽(節(jié)點)解析開始時調(diào)用- uri:xml文檔的命名空間
- localName:標簽的名字
- qName:帶命名空間的標簽的名字
- attributes:標簽的屬性集
characters(char[] ch, int start, int length):解析標簽的內(nèi)容的時候調(diào)用
- ch:當前讀取到的TextNode(文本節(jié)點)的字節(jié)數(shù)組
- start:字節(jié)開始的位置,為0則讀取全部
- length:當前TextNode的長度
- endElement(String uri, String localName, String qName):標簽(節(jié)點)解析結束后調(diào)用
- endDocument():文檔解析結束后調(diào)用,該方法只會調(diào)用一次
?重寫startDocument(),我們在這里初始化User集合,該集合用來存放解析出來的user
Log.e("startDocument", "startDocument()"); users = new ArrayList<User>();?重寫startElement,在startElement中先判斷當前的標簽是否user,如果是user標簽則說明接下來是一個user的信息,所以我們新建一個User對象用來存放這個user的信息,在這里我們得到當前user標簽的id屬性,封裝到user對象中。并記錄當前的標簽
Log.e("startElement", localName + "-startElement()");if ("user".equals(localName)) { // 是一個用戶for (int i = 0; i < attributes.getLength(); i++) {Log.e("attributes", "attribute_name:" + attributes.getLocalName(i)+ " attribute_value:" + attributes.getValue(i));user = new User();if("id".equals(attributes.getLocalName(i))){user.setId(Long.parseLong(attributes.getValue(i)));}}}currentTag = localName; // 把當前標簽記錄下來?重寫characters,在characters中解析出當前標簽的內(nèi)容,如果當前標簽為name標簽,則解析name標簽的內(nèi)容封裝到當前User對象的name屬性中,如果當前標簽為password標簽,則解析password標簽的內(nèi)容封裝到當前User對象的password屬性中
String value = new String(ch,start,length); // 將當前TextNode轉換為StringLog.e("characters", value+"");if("name".equals(currentTag)){ // 當前標簽為name標簽,該標簽無子標簽,直接將上面獲取到的標簽的值封裝到當前User對象中// 該節(jié)點為name節(jié)點user.setName(value);}else if("password".equals(currentTag)){ // 當前標簽為password標簽,該標簽無子標簽,直接將上面獲取到的標簽的值封裝到當前User對象中// 該節(jié)點為password節(jié)點user.setPassword(value);}?重寫endElement,在這個方法中判斷當前是否是user標簽的結束,如果是user標簽結束,則這個user信息解析結束,并將當前的User對象和當前的標簽重置
Log.e("endElement", localName + "-endElement()");if("user".equals(localName)){users.add(user);user = null;}currentTag = null;?重寫endDocument,這里直接給個空實現(xiàn),我們只需觀察Log輸出
Log.e("endDocument", "-endDocument()");XmlParseHandler.java完整代碼:
package com.example.saxparsexmldemo;import java.util.ArrayList; import java.util.List;import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler;import android.util.Log;public class XmlParseHandler extends DefaultHandler {private List<User> users;private String currentTag; // 記錄當前解析到的節(jié)點名稱private User user; // 記錄當前的user/*** 文檔解析結束后調(diào)用*/@Overridepublic void endDocument() throws SAXException {super.endDocument();Log.e("endDocument", "-endDocument()");}/*** 節(jié)點解析結束后調(diào)用* @param uri : 命名空間的uri* @param localName : 標簽的名稱* @param qName : 帶命名空間的標簽名稱*/@Overridepublic void endElement(String uri, String localName, String qName)throws SAXException {super.endElement(uri, localName, qName);Log.e("endElement", localName + "-endElement()");if("user".equals(localName)){users.add(user);user = null;}currentTag = null;}/*** 文檔解析開始調(diào)用*/@Overridepublic void startDocument() throws SAXException {super.startDocument();Log.e("startDocument", "startDocument()");users = new ArrayList<User>();}/*** 節(jié)點解析開始調(diào)用* @param uri : 命名空間的uri* @param localName : 標簽的名稱* @param qName : 帶命名空間的標簽名稱*/@Overridepublic void startElement(String uri, String localName, String qName,Attributes attributes) throws SAXException {super.startElement(uri, localName, qName, attributes);Log.e("startElement", localName + "-startElement()");if ("user".equals(localName)) { // 是一個用戶for (int i = 0; i < attributes.getLength(); i++) {Log.e("attributes", "attribute_name:" + attributes.getLocalName(i)+ " attribute_value:" + attributes.getValue(i));user = new User();if("id".equals(attributes.getLocalName(i))){user.setId(Long.parseLong(attributes.getValue(i)));}}}currentTag = localName; // 把當前標簽記錄下來}@Overridepublic void characters(char[] ch, int start, int length)throws SAXException {super.characters(ch, start, length); String value = new String(ch,start,length); // 將當前TextNode轉換為StringLog.e("characters", value+"");if("name".equals(currentTag)){ // 當前標簽為name標簽,該標簽無子標簽,直接將上面獲取到的標簽的值封裝到當前User對象中// 該節(jié)點為name節(jié)點user.setName(value);}else if("password".equals(currentTag)){ // 當前標簽為password標簽,該標簽無子標簽,直接將上面獲取到的標簽的值封裝到當前User對象中// 該節(jié)點為password節(jié)點user.setPassword(value);}}public List<User> getUsers() {return users;} }4、新建一個類XmlParseUtils.java,寫一個方法進行xml解析
public static List<User> getUsers() throws ParserConfigurationException, SAXException, IOException {// 加載文件返回文件的輸入流InputStream is = XmlParseUtils.class.getClassLoader().getResourceAsStream("users.xml");XmlParseHandler handler = new XmlParseHandler();// 1. 得到SAX解析工廠SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();// 2. 讓工廠生產(chǎn)一個sax解析器SAXParser newSAXParser = saxParserFactory.newSAXParser();// 3. 傳入輸入流和handler,解析newSAXParser.parse(is, handler);is.close();return handler.getUsers();}?其中第三步也可以通過XMLReader來完成
XMLReader xmlReader = newSAXParser.getXMLReader(); InputSource input = new InputSource(is); xmlReader.parse(input );到這里SAX解析XML的代碼完成了,OK,萬事具備,只欠測試了
這里我們用Android的單元測試,只需完成調(diào)用XmlParseUtils的getUsers()方法測試輸出即可
Android的單元測試(需要連接模擬器或者手機)
環(huán)境搭建
在 AndroidManifest.xml的根節(jié)點下面添加一個instrumentation
<instrumentation android:targetPackage="com.example.saxparsexmldemo" android:name="android.test.InstrumentationTestRunner"></instrumentation>在 AndroidManifest.xml的application節(jié)點下面添加uses-library
<uses-library android:name="android.test.runner"/>
- 在com.example.saxparsexmldemo下面新建一個測試類SAXParseXmlTest.java,寫一個測試方法
?OK,右鍵該類的testSAXgetUsers,Run As Android JUnit Test,我們可以看到XML解析成功
通過上面的這個小Demo的完成,我們可以看到SAX解析代碼不多也不難理解,關鍵是Handler的幾個方法的使用,即遇到什么符號會觸發(fā)什么事件,以及觸發(fā)過程,掌握了SAX的事件觸發(fā),那么就掌握了SAX解析XML,下面我們來分析一下SAX解析的原理或流程
四、SAX解析XML原理
在分析SAX解析原理之前,我們先看一下上面的demo的日志輸出
以users.xml的解析過程為例,結合上面的日志輸出,我們可以分析出SAX解析的流程
1、xml解析開始,startDocument被調(diào)用,這個方法在整個xml解析過程中調(diào)用了一次,所以我們可以在這個方法里面初為解析XML做一些準備,比如初始化變量?
2、解析每遇到一個標簽都會經(jīng)歷startElement-characters-endElement這個過程,即每一個標簽都會觸發(fā)startElement-characters-endElement?
3、通過users這個根節(jié)點的解析,user標簽解析以及name,password標簽解析過程,我們知道user標簽是users的子標簽,name和password標簽是user標簽的子標簽,我們知道當解析一個標簽的時候,如果該標簽有子標簽,則先回調(diào)用該標簽的startElement方法,這里面可以先得到該標簽的屬性信息,然后觸發(fā)characters解析該標簽的內(nèi)容(值),然后子標簽觸發(fā)startElement-characters-endElement(子標簽觸發(fā)),最后該標簽觸發(fā)endElement,該標簽解析結束
下面畫一個圖讓我們進一步理解SAX解析XML的原理:
對上圖做個簡單說明,當當前標簽有子標簽的時候,該標簽先觸發(fā)characters,然后子標簽觸發(fā)startElement-characters-endElement事件,這個過程可以理解為一個不斷遞歸的過程。
OK,SAX解析原理分析完了,最后用一句話描述SAX解析過程
startDocument-startElement-characters-endElement-endDocument
總結,SAX解析XML具有解析速度快,占用內(nèi)存少,對于Android等移動設備來說有巨大的優(yōu)勢,深入了解SAX的事件觸發(fā)機制是掌握SAX解析的關鍵,掌握了SAX的事件觸發(fā)就掌握了SAX解析XML
上面這篇文章由于個人理解,如果有理解錯的地方,歡迎大家指出,與君共勉,一起進步。
Demo下載地址:http://download.csdn.net/detail/ydxlt/9328309
下篇文章:【XML解析(二)】DOM解析XML
總結
以上是生活随笔為你收集整理的XML解析(一),SAX解析XML的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: XML解析(二),DOM解析XML
- 下一篇: 读取/书写Java的XML格式prope