android解析XML總結(SAX、Pull、Dom三種方式)附帶DOM4J、JDOM

摘要: 在android開發中,經常用到去解析xml文件,常見的解析xml的方式有一下三種:SAX、Pull、Dom解析方式。最近做瞭一個android版的CSDN閱讀器,用到瞭其中的兩種(sax,pull),今天對android解析xml的這三種方式進行一次 …
在android開發中,經常用到去解析xml文件,常見的解析xml的方式有一下三種:SAX、Pull、Dom解析方式。最近做瞭一個android版的CSDN閱讀器,用到瞭其中的兩種(sax,pull),今天對android解析xml的這三種方式進行一次總結。
     今天解析的xml示例(channels.xml)如下:
<?xml version="1.0" encoding="utf-8"?>
<channel>
<item id="0" url="http://www.baidu.com">百度item>
<item id="1" url="http://www.qq.com">騰訊item>
<item id="2" url="http://www.sina.com.cn">新浪item>
<item id="3" url="http://www.taobao.com">淘寶item>
channel>
一、使用sax方式解析
基礎知識:
     這種方式解析是一種基於事件驅動的api,有兩個部分,解析器和事件處理器,解析器就是XMLReader接口,負責讀取XML文檔,和向事件處理器發送事件(也是事件源),事件處理器ContentHandler接口,負責對發送的事件響應和進行XML文檔處理。
     下面是ContentHandler接口的常用方法
     public abstract void characters (char[] ch, int start, int length)
      這個方法來接收字符塊通知,解析器通過這個方法來報告字符數據塊,解析器為瞭提高解析效率把讀到的所有字符串放到一個字符數組(ch)中,作為參數傳遞給character的方法中,如果想獲取本次事件中讀取到的字符數據,需要使用start和length屬性。
    public abstract void startDocument () 接收文檔開始的通知
     public abstract void endDocument () 接收文檔結束的通知
    public abstract void startElement (String uri, String localName, String qName, Attributes atts) 接收文檔開始的標簽
    public abstract void endElement (String uri, String localName, String qName) 接收文檔結束的標簽
    在一般使用中為瞭簡化開發,在org.xml.sax.helpers提供瞭一個DefaultHandler類,它實現瞭ContentHandler的方法,我們隻想繼承DefaultHandler方法即可。
   另外SAX解析器提供瞭一個工廠類:SAXParserFactory,SAX的解析類為SAXParser 可以調用它的parser方法進行解析。
   看瞭些基礎以後開始上代碼吧(核心代碼,下載代碼在附件)
 1 public class SAXPraserHelper extends DefaultHandler {
 2
 3     final int ITEM = 0x0005;
 4
 5     List list;
 6     channel chann;
 7     int currentState = 0;
 8
 9     public List getList() {
10         return list;
11     }
12
13     /*
14      * 接口字符塊通知
15 */
16     @Override
17     public void characters(char[] ch, int start, int length)
18             throws SAXException {
19         // TODO Auto-generated method stub
20 // super.characters(ch, start, length);
21         String theString = String.valueOf(ch, start, length);
22         if (currentState != 0) {
23             chann.setName(theString);
24             currentState = 0;
25         }
26         return;
27     }
28
29     /*
30      * 接收文檔結束通知
31 */
32     @Override
33     public void endDocument() throws SAXException {
34         // TODO Auto-generated method stub
35         super.endDocument();
36     }
37
38     /*
39      * 接收標簽結束通知
40 */
41     @Override
42     public void endElement(String uri, String localName, String qName)
43             throws SAXException {
44         // TODO Auto-generated method stub
45         if (localName.equals("item"))
46             list.add(chann);
47     }
48
49     /*
50      * 文檔開始通知
51 */
52     @Override
53     public void startDocument() throws SAXException {
54         // TODO Auto-generated method stub
55         list = new ArrayList();
56     }
57
58     /*
59      * 標簽開始通知
60 */
61     @Override
62     public void startElement(String uri, String localName, String qName,
63             Attributes attributes) throws SAXException {
64         // TODO Auto-generated method stub
65         chann = new channel();
66         if (localName.equals("item")) {
67             for (int i = 0; i < attributes.getLength(); i++) {
68                 if (attributes.getLocalName(i).equals("id")) {
69                     chann.setId(attributes.getValue(i));
70                 } else if (attributes.getLocalName(i).equals("url")) {
71                     chann.setUrl(attributes.getValue(i));
72                 }
73             }
74             currentState = ITEM;
75             return;
76         }
77         currentState = 0;
78         return;
79     }
80 }
 1 private List getChannelList() throws ParserConfigurationException, SAXException, IOException
 2     {
 3         //實例化一個SAXParserFactory對象
 4         SAXParserFactory factory=SAXParserFactory.newInstance();
 5         SAXParser parser;
 6         //實例化SAXParser對象,創建XMLReader對象,解析器
 7         parser=factory.newSAXParser();
 8         XMLReader xmlReader=parser.getXMLReader();
 9         //實例化handler,事件處理器
10         SAXPraserHelper helperHandler=new SAXPraserHelper();
11         //解析器註冊事件
12         xmlReader.setContentHandler(helperHandler);
13         //讀取文件流
14         InputStream stream=getResources().openRawResource(R.raw.channels);
15         InputSource is=new InputSource(stream);
16         //解析文件
17         xmlReader.parse(is);
18         return helperHandler.getList();
19     }
從第二部分代碼,可以看出使用SAX解析XML的步驟:
1、實例化一個工廠SAXParserFactory
2、實例化SAXPraser對象,創建XMLReader 解析器
3、實例化handler,處理器
4、解析器註冊一個事件
5、讀取文件流
6、解析文件
或者以下一種理解:
》Sax定義
         SAX是一個解析速度快並且占用內存少的xml解析器,非常適合用於android等移動設備
         SAX全稱是Simple API for Xml,既是指一種接口,也是一個軟件包
         作為接口,sax是事件驅動型xml解析的一個標準接口
》Sax特點
        1. 解析效率高,占用內存少
        2.可以隨時停止解析
        3.不能載入整個文檔到內存
        4.不能寫入xml
        5.SAX解析xml文件采用的是事件驅動
—sax並不需要解析完 整個文檔,在按內容順序解析文檔的過程中,sax會判斷當前讀到的字符是否合法xml語法中的某部分,如果符合就會觸發事件
》Sax工作原理
         Sax的工作原理簡單的說,就是對文檔進行順序掃描,掃描到文檔(document)開始與結束,掃描到元素(element)開始、結束等地方時調用事件處理
         處理函數做相應動作,然後繼續掃描,直到文檔結束。
 
二、使用pull方式解析
基礎知識:
      在android系統中,很多資源文件中,很多都是xml格式,在android系統中解析這些xml的方式,是使用pul解析器進行解析的,它和sax解析一樣(個人感覺要比sax簡單點),也是采用事件驅動進行解析的,當pull解析器,開始解析之後,我們可以調用它的next()方法,來獲取下一個解析事件(就是開始文檔,結束文檔,開始標簽,結束標簽),當處於某個元素時可以調用XmlPullParser的getAttributte()方法來獲取屬性的值,也可調用它的nextText()獲取本節點的值。
其實以上描述,就是對整個解析步驟的一個描述,看看代碼吧
 1 private List<map> getData() {
 2         List<map> list = new ArrayList<map>();
 3         XmlResourceParser xrp = getResources().getXml(R.xml.channels);
 4
 5         try {
 6             // 直到文檔的結尾處
 7             while (xrp.getEventType() != XmlResourceParser.END_DOCUMENT) {
 8                 // 如果遇到瞭開始標簽
 9                 if (xrp.getEventType() == XmlResourceParser.START_TAG) {
10                     String tagName = xrp.getName();// 獲取標簽的名字
11                     if (tagName.equals("item")) {
12                         Map map = new HashMap();
13                         String id = xrp.getAttributeValue(null, "id");// 通過屬性名來獲取屬性值
14                         map.put("id", id);
15                         String url = xrp.getAttributeValue(1);// 通過屬性索引來獲取屬性值
16                         map.put("url", url);
17                         map.put("name", xrp.nextText());
18                         list.add(map);
19                     }
20                 }
21                 xrp.next();// 獲取解析下一個事件
22             }
23         } catch (XmlPullParserException e) {
24             // TODO Auto-generated catch block
25             e.printStackTrace();
26         } catch (IOException e) {
27             // TODO Auto-generated catch block
28             e.printStackTrace();
29         }
30
31         return list;
32     }
或者以下一種理解:
》pull解析器簡介
        1.pull解析器是android內置的解析器,解析原理與sax類似
        2.pull它提供瞭類似的事件。
              如:開始元素和結束元素事件,使用parse.next()可以進入下一個元素並觸發相應的事件,事件將作為數值代碼被發送
                      因此可以使用一個switch對感興趣的事件進行處理。當元素開始解析時,調用parser.nextText()方法獲取下一個Text類型節點的值
》pull與sax的不同之處
          1.pull讀取xml文件後觸發相應的事件調用方法返回的是數字。
          2.pull可以在程序中控制,想解析到哪裡就可以停止到哪裡
          3.Android中更推薦使用pull解析
》pull解析步驟
      1.創建解析器對象
          XmlPullParser paser = Xml.newPullParser();
      2.進行解析
             paser.setInput(input,"utf-8");
      3.產生第一個解析事件
              int eventType = paser.getEventType();
       4.可以使用循環判斷是否繼續解析
                while(eventType!=XmlPullParser.END_DOCUMENT){}
 
三、使用Dom方式解析
基礎知識:
     最後來看看Dom解析方式,這種方式解析自己之前也沒有用過(在j2ee開發中比較常見,沒有做過這方面的東西),在Dom解析的過程中,是先把dom全部文件讀入到內存中,然後使用dom的api遍歷所有數據,檢索想要的數據,這種方式顯然是一種比較消耗內存的方式,對於像手機這樣的移動設備來講,內存是非常有限的,所以對於比較大的XML文件,不推薦使用這種方式,但是Dom也有它的優點,它比較直觀,在一些方面比SAX方式比較簡單。在xml文檔比較小的情況下也可以考慮使用dom方式。
Dom方式解析的核心代碼如下:
 1 public static List getChannelList(InputStream stream)
 2     {
 3         List list=new ArrayList();
 4        
 5         //得到 DocumentBuilderFactory 對象, 由該對象可以得到 DocumentBuilder 對象
 6         DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
 7        
 8         try {
 9             //得到DocumentBuilder對象
10             DocumentBuilder builder=factory.newDocumentBuilder();
11             //得到代表整個xml的Document對象
12             Document document=builder.parse(stream);
13             //得到 "根節點"
14             Element root=document.getDocumentElement();
15             //獲取根節點的所有items的節點
16             NodeList items=root.getElementsByTagName("item"); 
17             //遍歷所有節點
18             for(int i=0;i<items.getlength();i++)
19             {
20                 channel chann=new channel();
21                 Element item=(Element)items.item(i);
22                 chann.setId(item.getAttribute("id"));
23                 chann.setUrl(item.getAttribute("url"));
24                 chann.setName(item.getFirstChild().getNodeValue());
25                 list.add(chann);
26             }
27            
28         } catch (ParserConfigurationException e) {
29             // TODO Auto-generated catch block
30             e.printStackTrace();
31         } catch (SAXException e) {
32             // TODO Auto-generated catch block
33             e.printStackTrace();
34         } catch (IOException e) {
35             // TODO Auto-generated catch block
36             e.printStackTrace();
37         }
38        
39         return list;
40     }
總結一下Dom解析的步驟(和sax類似)
1、調用 DocumentBuilderFactory.newInstance() 方法得到 DOM 解析器工廠類實例。
2、調用解析器工廠實例類的 newDocumentBuilder() 方法得到 DOM 解析器對象
3、調用 DOM 解析器對象的 parse() 方法解析 XML 文檔得到代表整個文檔的 Document 對象。
或者以下一種理解:
》DOM簡介
        dom全稱Document Object Model ,為xml文檔的已解析版本定義瞭一組接口。解析器讀入整個文檔,然後構建一個主流內存的樹結構,
         然後代碼就可以使用dom接口來操作這個樹結構
》DOM的特點
         >優點
                  1.整個文檔樹在內存中,便於操作;支持刪除、修改、重新排列等多種功能
                  2.通過樹形結構存取xml文檔
                  3.可以在樹的某個節點上向前或向後移動
           >缺點
                  1.將整個文檔調入內存(包括無用的節點),浪費時間和空間
            >適用場合
                  一旦解析瞭文檔還需多次訪問這些數據;硬件資源充足(內存,cpu)
》DOM解析步驟
      1.創建解析器工廠
      2.獲得解析器工廠
      3.接受一個xml文檔作為輸入參數名,並得到一個xml的文檔對象(Document)
      4.操作文檔對象
 
四、使用JDOM方式解析
基礎知識:
     JDOM是處理xml的純java api.使用具體類而不是接口.JDOM具有樹的遍歷,又有SAX的java規則.
優點:是基於樹的處理xml的java api,把樹加載到內存中.
      沒有向下兼容的限制,所以比DOM簡單.
      速度快.
      具有SAX的java 規則.
缺點:不能處理大於內存的文檔.
       JDOM表示XML文檔邏輯模型,不能保證每個字節真正變換.
       針對實例文檔不提供DTD與模式的任何實際模型.
       不支持於DOM中相應遍歷包.
 
 五、使用DOM4J方式解析
基礎知識:
     dom4j有更復雜的api,所以dom4j比jdom有更大的靈活性.
 
六、總結
       除以上三種外還有很多解析xml的方法,比如DOM4J、JDOM等等。但其基本的解析方式包含兩種,一種是事件驅動的(代表SAX),另一種方式是基於文檔結構(代表DOM)。其他的隻不過語法不一樣而已。
附(本文示例運行截屏):
 

You May Also Like