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