一、定義規則
-
XML數據結構定義

請記住上面的定義,后面我會用“標簽開始”、“文本”、“標簽結束”表示SAX正在處理哪部分XML數據 -
事件模型
為什么這里我要談到這個,因為SAX處理XML數據是采用事件的形式來處理,下面我們來簡單的做個介紹。
當我們處理XML數據中遇到一個開始標簽后SAX會告訴你“我遇到了一個開始標簽,這個標簽是XXXX”,等你作出反應后,它會繼續往下
這時它遇到了一段文本,SAX告訴你“我遇到了一段文本,是XXXX”,然后繼續等你作出反應,接着下去就遇到了結束標簽,SAX仍然會
告訴你“我到了一個結束標簽是XXX”。SAX就是以這樣的方式將整個XML數據全部處理完。
二、為何使用
-
節約內存
這里我要聲明我的目標設備是手機,而不是電腦等等。而手機的內存是很小的,同時也十分珍貴。或許你會說現在手機都是1GB、
2GB內存,根本不用着想。但是我們既然開發應用,當然是希望使用的人越多越好,而大多數手機設備是沒有那么多內存的,所以我們
需要盡量使我們開發的應用能夠適合於很多的設備。 -
效率高
手機不僅僅有着內存少的缺點,而且本身的CPU處理能力也是相對較慢的。所以我們要讓代碼的速度更快更快,否則用戶就會感覺
使用你的應用總是卡頓半天,從而會選擇其他更優秀的應用。而SAX在執行效率方面也是很客觀的,當然這個前提是你的代碼夠簡潔,而
不是把所有邏輯處理任務都放進處理XML數據的方法里面。
三、安卓如何使用
-
DefaultHandler類
這是安卓中內置的用於SAX處理XML的類,但是大多情況下我們都需要繼承該類重寫部分方法,才能達到處理XML數據的功能。 -
startDocument方法
這是第一個需要重寫的方法,每處理一個XML文檔都會響應一次。所以這個方法里可以寫需要初始化的代碼。 -
startElement方法
這是處理每個節點所觸發的方法,通過這個方法你可以直接當前處理的節點的名稱以及屬性。 -
characters方法
當處理一個節點之間的文本時候觸發該方法,但是該方法並不會告訴你當前文本的所屬標簽,而僅僅是告訴你文本內容。 -
endElement方法
遇到一個節點的結束標簽時,將會出發這個方法,並且會傳遞結束標簽的名稱。 -
endDocument方法
如果當前的XML文檔處理完畢后,將會觸發該方法,在此方法內你可以將最終的結果保存並且銷毀不需要使用的變量。
四、執行流程舉例
-
下面我將以下的XML文件為例,說明SAX具體如何處理XML文件。(部分文本因為是中文所以經過了URL編碼)
1 <notic> 2 <id>1</id> 3 <title>%3cs%3edsds%3c%2fs%3e</title> 4 <content>%e5%86%85%e5%ae%b91</content> 5 <author>1</author> 6 </notic>
2. 下面是具體的響應過程
| 方法名稱 | localName(標簽名稱) | ch[](文本名稱) |
| startDocument | -- | -- |
| startElement | notic | -- |
| startElement | id | -- |
| characters | -- | 1 |
| endElement | id | -- |
| startElement | title | -- |
| characters | -- | %3cs%3edsds%2c%2fs%3e |
| endElement | title | -- |
| startElement | content | -- |
| characters | -- | %e5%86%85%e5%ae%b91 |
| endElement | content | -- |
| startElement | author | -- |
| characters | -- | 1 |
| endElement | author | -- |
| endElement | notic | -- |
| endDocument | -- | -- |
3.通過上面的分析我們可以清楚的看到,它是如何處理XML文檔的,下面是列舉的一個順序圖:
1 <!-- startDocument --> 2 <notic> -> startElement(localName = 'notic') 3 <id> -> startElement(localName = 'id') 4 1 -> characters(ch[] = '1') 5 </id> -> endElement(localName = 'id') 6 <title> -> startElement(localName = 'title') 7 %3c... -> characters(ch[] = '略') 8 </title> -> endElement(localName = 'title') 9 <content> -> startElement(localName = 'content') 10 %e5... -> characters(ch[] = '略') 11 </content> -> endElement(localName = 'content') 12 <author> -> startElement(localName = 'author') 13 1 -> characters(ch[] = '1') 14 </author> -> endElement(localName = 'author') 15 </notic> -> endElement(localName = 'notic') 16 <!-- endDocument -->
五、實例
下面我們采用一個實例來學習如何使用SAX解析XML
-
下面是我們需要解析的XML文檔
1 <result> 2 <notic> 3 <id>1</id> 4 <title>%3cs%3edsds%3c%2fs%3e</title> 5 <content>%e5%86%85%e5%ae%b91</content> 6 <author>1</author> 7 <time>2013-11-01 12-00-00</time> 8 <section>%e4%bf%a1%e6%81%af%e5%a4%84</section> 9 <warn>False</warn> 10 </notic> 11 <notic> 12 <id>2</id> 13 <title>%e6%b5%8b%e8%af%952</title> 14 <content>%e5%86%85%e5%ae%b92</content> 15 <author>2</author> 16 <time>2013-11-02 12-00-00</time> 17 <section>%e4%bf%a1%e6%81%af%e5%a4%84</section> 18 <warn>True</warn> 19 </notic> 20 <notic> 21 <id>3</id> 22 <title>%e6%b5%8b%e8%af%953</title> 23 <content>%e5%86%85%e5%ae%b93</content> 24 <author>3</author> 25 <time>2013-11-03 12-00-00</time> 26 <section>%e4%bf%a1%e6%81%af%e5%a4%84</section> 27 <warn>False</warn> 28 </notic> 29 <notic> 30 <id>4</id> 31 <title>%e6%b5%8b%e8%af%954</title> 32 <content>%e5%86%85%e5%ae%b94</content> 33 <author>4</author> 34 <time>2013-11-04 12-00-00</time> 35 <section>%e4%bf%a1%e6%81%af%e5%a4%84</section> 36 <warn>False</warn> 37 </notic> 38 <notic> 39 <id>5</id> 40 <title>%e6%b5%8b%e8%af%955</title> 41 <content>%e5%86%85%e5%ae%b95</content> 42 <author>5</author> 43 <time>2013-11-05 12-00-00</time> 44 <section>%e4%bf%a1%e6%81%af%e5%a4%84</section> 45 <warn>False</warn> 46 </notic> 47 <notic> 48 <id>6</id> 49 <title>%e6%b5%8b%e8%af%956</title> 50 <content>%e5%86%85%e5%ae%b96</content> 51 <author>6</author> 52 <time>2013-11-06 12-00-00</time> 53 <section>%e4%bf%a1%e6%81%af%e5%a4%84</section> 54 <warn>True</warn> 55 </notic> 56 <notic> 57 <id>7</id> 58 <title>%e6%b5%8b%e8%af%956</title> 59 <content>%e5%86%85%e5%ae%b96</content> 60 <author>6</author> 61 <time>2013-11-06 12-00-00</time> 62 <section>%e4%bf%a1%e6%81%af%e5%a4%84</section> 63 <warn>True</warn> 64 </notic> 65 <notic> 66 <id>8</id> 67 <title>%e6%b5%8b%e8%af%956</title> 68 <content>%e5%86%85%e5%ae%b96</content> 69 <author>6</author> 70 <time>2013-11-06 12-00-00</time> 71 <section>%e4%bf%a1%e6%81%af%e5%a4%84</section> 72 <warn>True</warn> 73 </notic> 74 <notic> 75 <id>9</id> 76 <title>%e6%b5%8b%e8%af%956</title> 77 <content>%e5%86%85%e5%ae%b96</content> 78 <author>6</author> 79 <time>2013-11-06 12-00-00</time> 80 <section>%e4%bf%a1%e6%81%af%e5%a4%84</section> 81 <warn>True</warn> 82 </notic> 83 <notic> 84 <id>10</id> 85 <title>%e6%b5%8b%e8%af%956</title> 86 <content>%e5%86%85%e5%ae%b96</content> 87 <author>6</author> 88 <time>2013-11-06 12-00-00</time> 89 <section>%e4%bf%a1%e6%81%af%e5%a4%84</section> 90 <warn>True</warn> 91 </notic> 92 </result>
-
開始繼承DefaultHandler類
1 public class SAXForHandler extends DefaultHandler { 2 //用於保存當前處理的節點名稱 3 private String preTag; 4 //用於保存當前處理的節點數據 5 private Map<String,String> curMap; 6 //用於保存最終的結果 7 private ArrayList<Map<String,String>> result; 8 9 //重寫startDocument 10 public void startDocument() 11 { 12 //初始化 13 result = new ArrayList<Map<String,String>>(); 14 } 15 16 public void startElement(String uri,String localName,String qName,Attributes attributes) 17 { 18 //判斷當前節點是notic時表示是一項數據,創建一個新的項 19 if("notic".equals(localName)) 20 { 21 curMap = new HashMap<String,String>(); 22 } 23 //用於保存當前處理的節點名稱,用於后面判斷用 24 preTag = localName; 25 } 26 27 public void characters(char[] ch,int start,int length) 28 { 29 //從char[]數據轉換成String 30 String data = new String(ch,start,length); 31 if("id".equals(preTag)) 32 { 33 //表示當前的文本值是id標簽的 34 curMap.put("id",data); 35 }else if("title".equals(preTag)) 36 { 37 //表示當前的文本值是title標簽的 38 curMap.put("title",data); 39 }else if("content".equals(preTag)) 40 { 41 //表示當前的文本值是content標簽的 42 curMap.put("content",data); 43 } 44 else if("time".equals(preTag)) 45 { 46 //表示當前的文本值是time標簽的 47 curMap.put("time",data); 48 }else if("section".equals(preTag)) 49 { 50 //表示當前的文本值是section標簽的 51 curMap.put("section",data); 52 }else if("warn",equals(preTag)) 53 { 54 //表示當前的文本值是warn標簽的 55 curMap.put("warn",data); 56 } 57 } 58 59 public void endElement(String uri,String localName,String qName) 60 { 61 if("notic".equals(localName)) 62 { 63 //表示一條數據處理完畢 64 result.add(curMap); 65 curMap = null; 66 } 67 //每次處理完一個節點都要將preTag重置 68 preTag = null; 69 } 70 71 public void endDocument() 72 { 73 //XML處理結束,因為沒有使用到必須釋放的資源所以這里為空 74 } 75 }
-
如何使用該SAXForHandler
1 //in為InputStream表示讀取的XML文件流 2 SAXParserFactory spf = SAXParserFactory.newInstance(); 3 SAXParser sp = spf.newSAXParser(); 4 SAXForHandler sfh = new SAXForHandler(); 5 sp.parser(in,sfh); 6 //如果要想獲得處理后的結果 7 //可以公開一個方法,用來將result通過return的方法傳遞給調用者.
六、以下是本人開發的一個工具類,可以方便的通過代碼調整SAXForHandler來處理不同的XML
1 package com.ninesoft.newprower.xml; 2 3 import java.io.UnsupportedEncodingException; 4 import java.net.URLDecoder; 5 import java.util.ArrayList; 6 import java.util.HashMap; 7 import java.util.Map; 8 9 import org.xml.sax.Attributes; 10 import org.xml.sax.SAXException; 11 import org.xml.sax.helpers.DefaultHandler; 12 13 public class SaxForXmlHandler extends DefaultHandler { 14 private String[] _needTag; 15 private ArrayList<Map<String,String>> _notics; 16 private Map<String,String> current; 17 private String preTag; 18 private String _nodeTag; 19 20 //構造函數 21 public SaxForXmlHandler(String tag) 22 { 23 this._nodeTag = tag; 24 } 25 public SaxForXmlHandler(String[] need) 26 { 27 this._needTag = need; 28 } 29 public SaxForXmlHandler(String tag,String[] need) 30 { 31 this._nodeTag = tag; 32 this._needTag = need; 33 } 34 35 //獲取設置每個節點數據的標簽名稱 36 public void setNodeTag(String tag) 37 { 38 this._nodeTag = tag; 39 } 40 public String getNodeTag() 41 { 42 return this._nodeTag; 43 } 44 45 //獲取設置包含數據的標簽名稱數組 46 public void setNeedTag(String[] need) 47 { 48 this._needTag = need; 49 } 50 public String[] getNeedTag() 51 { 52 return this._needTag; 53 } 54 55 //獲得最終處理后的數據 56 public ArrayList<Map<String,String>> getResult() 57 { 58 return this._notics; 59 } 60 61 //文檔開始 62 @Override 63 public void startDocument() throws SAXException { 64 this._notics = new ArrayList<Map<String,String>>(); 65 this.preTag = null; 66 this.current = null; 67 if(this._nodeTag == null){ 68 throw new IllegalArgumentException("節點標簽名稱未賦值"); 69 }else if(this._needTag == null){ 70 throw new IllegalArgumentException("數據標簽數據未賦值"); 71 } 72 super.startDocument(); 73 } 74 75 //節點開頭 76 @Override 77 public void startElement(String uri, String localName, String qName, 78 Attributes attributes) throws SAXException { 79 if(_nodeTag.equals(localName)){ 80 //實例化一個Map對象 81 current = new HashMap<String,String>(); 82 } 83 //將當前處理的標簽名稱保存至preTag中 84 preTag = localName; 85 } 86 87 //節點中的文本 88 @Override 89 public void characters(char[] ch, int start, int length) 90 throws SAXException { 91 //提取標簽中的文本 92 String data = new String(ch,start,length); 93 String dedata = ""; 94 for(String item : this._needTag) 95 { 96 if(item.equals(preTag)) 97 { 98 try { 99 //將數據進行URL解碼 100 dedata = URLDecoder.decode(data,"UTF-8"); 101 } catch (UnsupportedEncodingException e) { 102 dedata = data; 103 }finally{ 104 //將當前的數據放入map對象中 105 current.put(item, dedata); 106 } 107 return; 108 } 109 } 110 } 111 112 //節點結束 113 @Override 114 public void endElement(String uri, String localName, String qName) 115 throws SAXException { 116 if(this._nodeTag.equals(localName)) 117 { 118 //將當前map對象放入ArrayList對象中 119 this._notics.add(current); 120 current = null; 121 } 122 //將當前標簽設置為null 123 preTag = null; 124 } 125 126 //文檔結束 127 @Override 128 public void endDocument() throws SAXException { 129 current = null; 130 preTag = null; 131 super.endDocument(); 132 } 133 }
