解析XML的方式有不少種,你們比較熟悉的可能就是DOM解析。 java
DOM(文件對象模型)解析:解析器讀入整個文檔,而後構建一個駐留內存的樹結構,而後代碼就能夠根據DOM接口來操做這個樹結構了。 android
優勢:整個文檔讀入內存,方便操做:支持修改、刪除和重現排列等多種功能。 ide
缺點:將整個文檔讀入內存中,保留了過多的不須要的節點,浪費內存和空間。 函數
使用場合:一旦讀入文檔,還須要屢次對文檔進行操做,而且在硬件資源充足的狀況下(內存,CPU)。 性能
爲了解決DOM解析存在的問題,就出現了SAX解析。其特色爲: this
優勢:不用實現調入整個文檔,佔用資源少。尤爲在嵌入式環境中,如android,極力推薦使用SAX解析。 spa
缺點:不像DOM解析同樣將文檔長期駐留在內存中,數據不是持久的。若是事件事後沒有保存數據,數據就會丟失。 .net
使用場合:機器有性能限制。 xml
SAX解析XML文檔採用事件驅動模式。什麼是事件驅動模式?它將XML文檔轉換成一系列的事件,由單獨的事件處理器來決定如何處理。 對象
基於事件驅動的處理模式主要是基於事件源和事件處理器(或者叫監聽器)來工做的。一個能夠產生事件的對象叫作事件源,而一個能夠針對事件作出響應的對象就被叫作事件處理器。
在SAX接口中,事件源是org.xml.sax包中的XMLReader,他經過parse()方法開始解析XML文檔,並根據文檔內容產生事件。而事件處理器則是org.xml.sax包中的ContentHandler、DTDHandler、ErrorHandler,以及EntityResolver這四個接口。他們分別處理事件源在解析過程當中產生不一樣類的事件(其中DTDHandler爲解析文檔DTD時所用)。詳細介紹以下表:
在上述四個接口中,最重要的就是ContentHandler這個接口,下面是對這個接口方法的說明:
//設置一個能夠定位文檔內容事件發生位置的定位器對象
public void setDocumentLocator(Locator locator)
//用於處理文檔解析開始事件
public void startDocument()throws SAXException
//處理元素開始事件,從參數中能夠得到元素所在名稱空間的uri,元素名稱,屬性類表等信息
public void startElement(String namespacesURI , String localName , String qName , Attributes atts) throws SAXException
//處理元素結束事件,從參數中能夠得到元素所在名稱空間的uri,元素名稱等信息
public void endElement(String namespacesURI , String localName , String qName) throws SAXException
//處理元素的字符內容,從參數中能夠得到內容
public void characters(char[] ch , int start , int length) throws SAXException
這裏再介紹下XMLReader中的方法。
//註冊處理XML文檔解析事件ContentHandler
public void setContentHandler(ContentHandler handler)
//開始解析一個XML文檔
public void parse(InputSorce input) throws SAXException
SAX實現實體解析的步驟:
在android中使用SAX是有跡可循的,徹底能夠按照下面的方法就能夠輕鬆找到xml裏的tag,而後獲得想要的內容。具體實現步驟以下:
(一)第一步:新建一個工廠類SAXParserFactory,代碼以下:
SAXParserFactory factory = SAXParserFactory.newInstance();
(二)第二步:讓工廠類產生一個SAX的解析類SAXParser,代碼以下:
SAXParser parser = factory.newSAXParser();
(三)第三步:從SAXPsrser中獲得一個XMLReader實例,代碼以下:
XMLReader reader = parser.getXMLReader();
(四)第四步:把本身寫的handler註冊到XMLReader中,通常最重要的就是ContentHandler,代碼以下:
RSSHandler handler = new RSSHandler();
reader.setContentHandler(handler);
複製代碼
(五)第五步:將一個xml文檔或者資源變成一個java能夠處理的InputStream流後,解析正式開始,代碼以下:
parser.parse(is);
上面幾個步驟中,最重要、最關鍵的就是第四步,handler的實現.
下面經過一個RSS解析的例子說明handler的實現:
咱們先是本身見一個rss的xml文檔,實現本地解析,新建的rss文檔以下:
<?xml version="1.0" encoding="UTF-8"?>
<channel>
<title>RSS 解析練習</title>
<description>hehehaha</description>
<link>http://www.cnblogs.com/felix-hua/</link>
<language>zh-cn</language>
<item>
<title><![CDATA[頭條]]></title>
<link>http://mc.cz001.com.cn/images/menu/23_active.png</link>
<category>0</category>
<description>描述詳細信息的</description>
<pubDate>2012-01-09</pubDate>
</item>
<item>
<title><![CDATA[新聞]]></title>
<link>http://mc.cz001.com.cn/images/menu/23_active.png</link>
<category>0</category>
<description>描述詳細信息的</description>
<pubDate>2012-01-09</pubDate>
</item>
<item>
<title><![CDATA[首頁]]></title>
<link>http://mc.cz001.com.cn/images/menu/23_active.png</link>
<category>0</category>
<description>描述詳細信息的</description>
<pubDate>2012-01-09</pubDate>
</item>
<item>
<title><![CDATA[財經]]></title>
<link>http://mc.cz001.com.cn/images/menu/23_active.png</link>
<category>0</category>
<description>描述詳細信息的</description>
<pubDate>2012-01-09</pubDate>
</item>
而後咱們能夠創建兩個實體類:
一、RSSFeed,與完整的xml文檔相對應;
二、RSSItem,與item標籤內的信息相對應。
這樣在解析xml時,咱們就能夠把解析出來的信息放到實體類裏,而後直接操做實體類就能夠了。下面給出代碼:
RSSFeed.java
mport java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
public class RSSFeed {
private String title;
private int itemcount;
private List<RSSItem> itemlist;
public RSSFeed(){
itemlist = new Vector<RSSItem>(0);
}
/**
* 負責將一個RSSItem加入到RSSFeed類中
* @param item
* @return
*/
public int addItem(RSSItem item){
itemlist.add(item);
itemcount++;
return itemcount;
}
public RSSItem getItem(int location){
return itemlist.get(location);
}
public List<RSSItem> getAllItems(){
return itemlist;
}
/**
* 負責從RSSFeed類中生成列表所須要的數據
* @return
*/
public List getAllItemForListView(){
List<Map<String, Object>> data = new ArrayList<Map<String,Object>>();
int size = itemlist.size();
for(int i=0 ; i<size ; i++){
HashMap<String , Object> item = new HashMap<String, Object>();
item.put(RSSItem.TITLE, itemlist.get(i).getTitle());
item.put(RSSItem.PUBDATE, itemlist.get(i).getPubdate());
data.add(item);
}
return data;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getItemcount() {
return itemcount;
}
public void setItemcount(int itemcount) {
this.itemcount = itemcount;
}
public List<RSSItem> getItemlist() {
return itemlist;
}
public void setItemlist(List<RSSItem> itemlist) {
this.itemlist = itemlist;
}
}
public class RSSItem {
public static String TITLE = "title";
public static String PUBDATE = "pubdate";
public String title;
public String description;
public String link;
public String category;
public String pubdate;
public RSSItem() {
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getLink() {
return link;
}
public void setLink(String link) {
this.link = link;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public String getPubdate() {
return pubdate;
}
public void setPubdate(String pubdate) {
this.pubdate = pubdate;
}
}
下面就是最最重要的地方了,創建本身的ContentHandler.代碼以下:
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import com.sax.org.entity.RSSFeed;
import com.sax.org.entity.RSSItem;
public class RSSHandler extends DefaultHandler{
RSSFeed RssFeed;
RSSItem RssItem;
final int RSS_TITLE = 1;
final int RSS_LINK = 2;
final int RSS_DESCRIPTION = 3;
final int RSS_CATEGORY = 4;
final int RSS_PUBDATE = 5;
int currentstate = 0;
public RSSHandler(){}
public RSSFeed getFeed(){
return RssFeed;
}
@Override
public void startDocument() throws SAXException {
// TODO Auto-generated method stub
RssFeed = new RSSFeed();
RssItem = new RSSItem();
}
@Override
public void endDocument() throws SAXException {
// TODO Auto-generated method stub
}
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
// TODO Auto-generated method stub
if(localName.equals("channel")){
currentstate = 0;
return;
}
if(localName.equals("item")){
RssItem = new RSSItem();
return;
}
if(localName.equals("title")){
currentstate = RSS_TITLE;
return;
}
if(localName.equals("description")){
currentstate = RSS_DESCRIPTION;
return;
}
if(localName.equals("link")){
currentstate = RSS_LINK;
return;
}
if(localName.equals("category")){
currentstate = RSS_CATEGORY;
return;
}
if(localName.equals("pubDate")){
currentstate = RSS_PUBDATE;
return;
}
currentstate = 0;
}
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
// TODO Auto-generated method stub
if(localName.equals("item")){
RssFeed.addItem(RssItem);
return;
}
}
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
// TODO Auto-generated method stub
String theString = new String(ch, start, length);
switch(currentstate){
case RSS_TITLE:
RssItem.setTitle(theString);
currentstate = 0;
break;
case RSS_DESCRIPTION:
RssItem.setDescription(theString);
currentstate = 0;
break;
case RSS_LINK:
RssItem.setLink(theString);
currentstate = 0;
break;
case RSS_PUBDATE:
RssItem.setPubdate(theString);
currentstate = 0;
break;
case RSS_CATEGORY:
RssItem.setCategory(theString);
currentstate = 0;
break;
default:
return;
}
}
}
就上面的代碼分析,實現一個ContentHandler通常要一下幾個步驟:
一、聲明一個類,繼承DefaultHandler。DefaultHandler是一個基類,這個類裏面簡單實現了一個ContentHandler。咱們只須要重寫裏面的方法便可。
二、重寫 startDocument() 和 endDocument(),通常解析將正式解析以前的一些初始化工做放到startDocument()裏面,收尾的工做放到endDocument()裏面。
三、重寫startElement(),XML解析器遇到XML裏面的tag時就會調用這個函數。常常在這個函數內是經過localName倆進行判斷而操做一些數據。
四、重寫characters()方法,這是一個回調方法。解析器執行完startElement()後,解析完節點的內容後就會執行這個方法,而且參數ch[]就是節點的內容。這個例子裏咱們根據currentstate的不一樣,來判斷當前那個tag的內容,並放到合適的實體類中。
五、重寫endElement()方法,這個方法與startElement()相對應,解析完一個tag節點後,執行這個方法。再找個例子中,若是解析一個item結束,就將RSSIiem添加到RSSFeed中。