Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 詳解android使用SAX解析XML文件

詳解android使用SAX解析XML文件

編輯:關於Android編程

解析XML的方式有很多種,大家比較熟悉的可能就是DOM解析。

DOM(文件對象模型)解析:解析器讀入整個文檔,然後構建一個駐留內存的樹結構,然後代碼就可以根據DOM接口來操作這個樹結構了。

優點:整個文檔讀入內存,方便操作:支持修改、刪除和重現排列等多種功能。

缺點:將整個文檔讀入內存中,保留了過多的不需要的節點,浪費內存和空間。

使用場合:一旦讀入文檔,還需要多次對文檔進行操作,並且在硬件資源充足的情況下(內存,CPU)。

為了解決DOM解析存在的問題,就出現了SAX解析。其特點為:

優點:不用實現調入整個文檔,占用資源少。尤其在嵌入式環境中,如android,極力推薦使用SAX解析。

缺點:不像DOM解析一樣將文檔長期駐留在內存中,數據不是持久的。如果事件過後沒有保存數據,數據就會丟失。

使用場合:機器有性能限制。

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>

建好後,我們命名為rssxml.xml,然後放到項目的根目錄下:

然後我們可以建立兩個實體類:

1、RSSFeed,與完整的xml文檔相對應;

2、RSSItem,與item標簽內的信息相對應。

這樣在解析xml時,我們就可以把解析出來的信息放到實體類裡,然後直接操作實體類就可以了。下面給出代碼:

RSSFeed.java

package com.sax.org.entity;

import 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;
  }
  
}

RSSItem.java

package com.sax.org.entity;

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.看下面的代碼:

RSSHandler.java

package com.sax.org.handler;

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一般要一下幾個步驟:

1、聲明一個類,繼承DefaultHandler。DefaultHandler是一個基類,這個類裡面簡單實現了一個ContentHandler。我們只需要重寫裡面的方法即可。

2、重寫 startDocument() 和 endDocument(),一般解析將正式解析之前的一些初始化工資放到startDocument()裡面,收尾的工作放到endDocument()裡面。

3、重寫startElement(),XML解析器遇到XML裡面的tag時就會調用這個函數。經常在這個函數內是通過localName倆進行判斷而操作一些數據。

4、重寫characters()方法,這是一個回調方法。解析器執行完startElement()後,解析完節點的內容後就會執行這個方法,並且參數ch[]就是節點的內容。這個例子裡我們根據currentstate的不同,來判斷當前那個tag的內容,並放到合適的實體類中。

5、重寫endElement()方法,這個方法與startElement()相對應,解析完一個tag節點後,執行這個方法。再找個例子中,如果解析一個item結束,就將RSSIiem添加到RSSFeed中。

最後我們實現一個activity來展現解析的結果:

 package com.sax.org;

import java.io.IOException;
import java.net.URL;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

import android.app.Activity;
import android.os.Bundle;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;

import com.sax.org.entity.RSSFeed;
import com.sax.org.entity.RSSItem;
import com.sax.org.handler.RSSHandler;

public class SAXReaderActivity extends Activity {
  /** Called when the activity is first created. */

  public String rssUrl = "http://mc.cz001.com.cn/a/indexconfig/index.rss";
  public RSSFeed feed;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    feed = getFeed(rssUrl);
    showList();
  }

  public RSSFeed getFeed(String rssUrl) {
    try {
// 這裡我們實現了本地解析,所以注掉了這個取網絡數據的。
//      URL url = new URL(rssUrl);
      SAXParserFactory factory = SAXParserFactory.newInstance();
      SAXParser parser = factory.newSAXParser();
      XMLReader reader = parser.getXMLReader();
      RSSHandler handler = new RSSHandler();
      reader.setContentHandler(handler);
      InputSource is = new InputSource(this.getClassLoader().getResourceAsStream("rssxml.xml"));//取得本地xml文件
      reader.parse(is);
      return handler.getFeed();
    } catch (ParserConfigurationException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (SAXException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    return null;
  }

  public void showList() {
    ListView rsslistview = (ListView) findViewById(R.id.rssList);
    TextView rsstitle = (TextView) findViewById(R.id.rsstitle);
    if (feed == null) {
      rsstitle.setText("訪問失敗...");
      return;
    }
    SimpleAdapter adapter = new SimpleAdapter(this,
        feed.getAllItemForListView(),
        android.R.layout.simple_list_item_2, new String[] {
            RSSItem.TITLE, RSSItem.PUBDATE }, new int[] {
            android.R.id.text1, android.R.id.text2 });
    rsslistview.setAdapter(adapter);
  }
}

展示下運行結果:

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持本站。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved