Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android系列之網絡(四)----SAX方式解析XML數據

Android系列之網絡(四)----SAX方式解析XML數據

編輯:關於Android編程

一、XML的引入:   XML,可擴展標記語言 (Extensible Markup Language) ,用於標記電子文件使其具有結構性的標記語言,可以用來標記數據、定義數據類型,是一種允許用戶對自己的標記語言進行定義的源語言,這是百度百科的解釋。而XML是一種在Internet中傳輸數據的常見格式,它與HTML一樣,都是SGML(標准通用標記語言),無論你是需要通過Internet訪問數據,或者發送數據給Web服務,都可能需要用到XML的知識。恰恰Android應用程序需要和網絡交互,否則只是一款單機的無互動的應用程序,所以在Android應用程序開發的過程中使用到XML是很有必要的。   由於XML的擴展性強,致使它需要有穩定的基礎規則來支持擴展,該語法規則需要注意的是:   開始和結束標簽匹配。 嵌套標簽不能相互嵌套。 區分大小寫。 XML的結構解析如下:   節點 元素 屬性和屬性值 格式如下:   <標記名稱 屬性名1="屬性值1" 屬性名1="屬性值1" ……>內容</標記名稱>       二、Android中的XML解析的分類:   Android平台最大的優勢在於,上層應用基本可以利用Java編程語言開發,Java平台支持通過許多不同的方式來使用XML,並且大多數與XML相關的API已經在Android系統上得到了完全的支持。但是因為Android這個移動設備的局限性,一般僅考慮使用三種方式解析XML:   DOM,Document Object Model,文檔對象模型方式,解析完的XML將生成一個樹狀結構的對象。 SAX,simple API for  Xml,以事件的形式通知程序,對XML進行解析。 XML PULL,類似於SAX方式,程序以拉取的方式對XML進行解析。     三、SAX解析介紹:   SAX是一個解析速度快並且占用內存少的xml解析器,非常適合用於Android等移動設備。 SAX解析XML文件采用的是事件驅動,也就是說,它並不需要解析完整個文檔,在按內容順序解析文檔的過程中,SAX會判斷當前讀到的字符是否合法XML語法中的某部分,如果符合就會觸發事件。所謂事件,其實就是一些回調(callback)方法,這些方法(事件)定義在ContentHandler接口。   使用SAX的優點:   因為SAX的優勢是流的方式處理,當遇到一個標簽的時候,並不會記錄下之前所碰到的標簽。也就是說,在每個節點讀取會觸發的startElement()方法中,所能知道的信息,僅僅是當前的簽名的名字和屬性,至於標簽嵌套的結構,上層標簽的名字,是否有子元素與其他結構相關的信息,都是不知道的。   使用SAX解析XML的簡單步驟:   新建一個類MyHandler,繼承自DefaultHandler,並重寫DefaultHandler中的特有方法,解析XML的工作在此類中完成。 實例化一個SAX解析器的工廠對象,SAXParserFactory對象,使用SAXParserFactory.newInstance()方法獲取。 利用SAXParserFactory.newSAXParser()獲得SAX解析器對象SAXParser。 實例化MyHandler類,傳入需要解析的節點名稱。 使用SAXParser.parse()方法設置待解析的XML流和XML解析對象。 最後從MyHandler對象中獲得解析結果。 現在詳細講解一下上面提到的類的作用:   DefaultHandler類是SAX2事件處理程序的默認基類。它繼承了EntityResolver、DTDHandler、ContentHandler和ErrorHandler這四個接口。包含這四個接口的所有方法,所以我們在編寫事件處理程序時,可以不用直接實現這四個接口,而繼承該類,然後重寫我們需要的方法。   而在繼承DefaultHandler的類中,需要重寫以下五個方法:   復制代碼 public void startDocument() 當遇到文檔的開頭的時候,調用這個方法,可以在其中做一些預處理的工作。    public void startElement(String namespaceURI, String localName, String qName, Attributes attributes) 當讀到一個開始標簽的時候,會觸發這個方法,再次獲得元素的屬性。namespaceURI就是命名空間,localName是不帶命名空間前綴的標簽名,qName是帶命名空間前綴的標簽名。通過attributes可以得到所有的屬性名和相應的值。要注意的是SAX中一個重要的特點就是它的流式處理,當遇到一個標簽的時候,它並不會紀錄下以前所碰到的標簽,也就是說,在startElement()方法中,所有你所知道的信息,就是標簽的名字和屬性,至於標簽的嵌套結構,上層標簽的名字,是否有子元屬等等其它與結構相關的信息,都是不得而知的,都需要你的程序來完成。這使得SAX在編程處理上沒有DOM來得那麼方便。    public void characters(char[] ch, int start, int length) 這個方法用來處理在XML文件中讀到的內容,第一個參數用於存放文件的內容,後面兩個參數是讀到的字符串在這個數組中的起始位置和長度,使用new String(ch,start,length)就可以獲取內容。    public void endElement(String uri, String localName, String name) 和startElement()方法相對應,在遇到結束標簽的時候,調用這個方法。   public void endDocument() 和startDocument()方法相對應。當文檔結束的時候,調用這個方法,可以在其中做一些善後的工作。  復制代碼 我們通過一個XML文件來講解一下上面的五個方法在什麼時候被執行:   <?xml version="1.0" encoding="utf-8"?>                  startDocument   <persons>                                                             startElement     <person id="01">                                             startElement         <name nameid="1">                                   startElement           smyh                                                         characters           </name>                                                   endElement              <age>                                                        startElement               22                                                             characters             </age>                                                        endElement     </person>                      endElement          </persons>                        endElement   SAXParserFactory類,定義了一個工廠API,使應用程序能夠配置和獲得基於SAX的解析器以解析XML文檔。它只有一個protected的構造方法(單例模式),所以需要使用靜態的newInstance()方法來回的SAXParserFactory()對象。使用SAXParserFactory可以通過調用.newSAXParser()方法獲得一個SAXParser,通過SAXParser對象可以執行parser()方法,通過傳遞的參數設定XML流和解析器類。       四、SAX解析XML的步驟:   現在通過一個示例程序來講解一下SAX是怎麼解析XML文件的,這個示例程序是運行在Android平台上的,為了模擬真實情況,在tomcat服務器上放置了一個靜態的XML文件,即在D:\apache-tomcat-8.0.14\webapps\ROOT目錄中新建一個smyhvae.xml文件,xml文件內容如下:   復制代碼 <?xml version="1.0" encoding="UTF-8"?> <persons>     <person id="01">         <name>smyh</name>         <age>22</age>     </person>     <person id="02">         <name>vae</name>         <age>24</age>     </person> </persons> 復制代碼 注:關於tomcat服務器的配置,如果不清楚的話,請參照本人另外一篇博客:Android系列之網絡(三)----使用HttpClient發送HTTP請求(分別通過GET和POST方法發送數據)   因為我電腦的IP地址是192.168.1.112。現在我們在浏覽器輸入http://192.168.1.112:8080/smyhvae.xml,顯示效果如下:       現在我們需要做的是:通過Android程序去獲取並解析這段XML數據。在這個示例程序中,讀取person節點的值。因為是Android程序,所以別忘了賦予其訪問網絡的權限。   整個Android的工程結構如下:       (1)【新建工具類HttpUtils】通過URLHttpConnection獲取服務器上的XML流:   我們將其寫成工具類,代碼如下:   復制代碼  1 package com.example.androidsaxxml.http;  2   3 import java.io.InputStream;  4 import java.net.HttpURLConnection;  5 import java.net.URL;  6   7 //工具類:通過URLHttpConnection獲取服務器上的XML流  8 public class HttpUtils {  9  10     public HttpUtils() { 11     } 12      13     //方法:返回的InputStream對象就是服務器返回的XML流。 14     public static InputStream getXML(String path) {//參數path:之後將在MainActivity中指定具體的url鏈接 15         try { 16             URL url=new URL(path); 17             if(url!=null) 18             { 19                 HttpURLConnection connection=(HttpURLConnection)url.openConnection(); 20                 connection.setDoInput(true); 21                 connection.setConnectTimeout(3000); 22                 connection.setRequestMethod("GET"); 23                 int requesetCode=connection.getResponseCode(); 24                 if(requesetCode==200) 25                 { 26                     //如果執行成功,返回HTTP響應流 27                     return connection.getInputStream(); 28                 } 29             } 30         } catch (Exception e) { 31             // TODO: handle exception 32         }         33         return null; 34     } 35 }  復制代碼 (2)【新建類MyHandler】新建子類MyHandler,繼承DefaultHandler類:用來解析xml   sax解析xml最重要的步驟就是定義一個我們自己的Handler處理類,並讓其繼承 DefaultHandler 這個類,然後在裡面重寫其回調方法,在這些回調方法裡來做我們的xml解析。代碼如下:   復制代碼  1 package com.example.androidsaxxml.handler;  2   3 import java.util.ArrayList;  4 import java.util.HashMap;  5 import java.util.List;  6   7 import org.xml.sax.Attributes;  8 import org.xml.sax.SAXException;  9 import org.xml.sax.helpers.DefaultHandler; 10  11  12 //類:MyHandler,繼承DefaultHandler類,用於解析XML數據。 13 //之後在MainActivity中通過設定具體的nodeName來實例化MyHandler 14 public class MyHandler extends DefaultHandler { 15     private List<HashMap<String, String>> list = null; //解析後的XML內容 16     private HashMap<String, String> map = null;  //存放當前需要記錄的節點的XML內容 17     private String currentTag = null;//當前讀取的XML節點 18     private String currentValue = null;//當前節點的XML文本值 19     private String nodeName = null;//需要解析的節點名稱 20      21     public MyHandler(String nodeName) { 22         // 設置需要解析的節點名稱 23         this.nodeName = nodeName; 24     } 25      26     @Override 27     public void startDocument() throws SAXException { 28         // 接收文檔開始的通知 29         // 實例化ArrayList用於存放解析XML後的數據 30         list = new ArrayList<HashMap<String, String>>(); 31     } 32      33     @Override 34     public void startElement(String uri, String localName, String qName, 35             Attributes attributes) throws SAXException { 36         // 接收元素開始的通知         37         if (qName.equals(nodeName)) { 38             //如果當前運行的節點名稱與設定需要讀取的節點名稱相同,則實例化HashMap 39             map = new HashMap<String, String>(); 40         } 41         //Attributes為當前節點的屬性值,如果存在屬性值,則屬性值也讀取。 42         if (attributes != null && map != null) { 43             for (int i = 0; i < attributes.getLength(); i++) { 44                 //讀取到的屬性值,插入到Map中。 45                 map.put(attributes.getQName(i), attributes.getValue(i)); 46             } 47         } 48         //記錄當前節點的名稱。 49         currentTag = qName; 50     } 51      52     @Override 53     public void characters(char[] ch, int start, int length) 54             throws SAXException { 55         // 接收元素中字符數據的通知。 56         //當前節點有值的情況下才繼續執行 57         if (currentTag != null && map != null) { 58             //獲取當前節點的文本值,ch這個直接數組就是存放的文本值。 59             currentValue = new String(ch, start, length); 60             if (currentValue != null && !currentValue.equals("") 61                     && !currentValue.equals("\n")) { 62                 //讀取的文本需要判斷不能為null、不能等於”“、不能等於”\n“ 63                 map.put(currentTag, currentValue); 64             } 65         } 66         //讀取完成後,需要清空當前節點的標簽值和所包含的文本值。 67         currentTag = null; 68         currentValue = null; 69     } 70      71     @Override 72     public void endElement(String uri, String localName, String qName) 73             throws SAXException { 74         // 接收元素結束的通知。 75         if (qName.equals(nodeName)) { 76             //如果讀取的結合節點是我們需要關注的節點,則把map加入到list中保存 77             list.add(map); 78             //使用之後清空map,開始新一輪的讀取person。 79             map = null; 80         } 81     } 82      83     //方法:獲取解析之後的數據 84     public List<HashMap<String, String>> getList() { 85         return list; 86     } 87 } 復制代碼 (3)【新建類SaxService】實例化一個SAX解析器的工廠對象:SAXParserFactory   需要一個調用SAXParser對象的類,這裡新建一個SaxService類,實例化SAXParserFactory用於設定XML流和解析器,也就是在這裡調用了上一步中的MyHandler類。代碼如下:   復制代碼  1 package com.example.androidsaxxml.service;  2   3 import java.io.InputStream;  4 import java.util.HashMap;  5 import java.util.List;  6   7 import javax.xml.parsers.SAXParser;  8 import javax.xml.parsers.SAXParserFactory;  9  10 import com.example.androidsaxxml.handler.MyHandler; 11  12  13 //類:用於實例化例化一個SAX解析器的工廠對象:SAXParserFactory 14 public class SaxService { 15  16     public SaxService() { 17         // TODO Auto-generated constructor stub 18     } 19      20     //方法:解析xml數據並返回,返回值類型是HashMap 21     public static List<HashMap<String, String>> readXML(InputStream inputStream,String nodeName) 22     { 23         try { 24             //實例化SAX工廠類 25             SAXParserFactory factory=SAXParserFactory.newInstance(); 26             //實例化SAX解析器。 27             SAXParser sParser=factory.newSAXParser(); 28             //實例化工具類MyHandler,設置需要解析的節點 29             MyHandler myHandler=new MyHandler(nodeName); 30             // 開始解析 31             sParser.parse(inputStream, myHandler); 32             // 解析完成之後,關閉流 33             inputStream.close(); 34             //返回解析結果。 35             return myHandler.getList();  //在這裡返回解析之後的數據 36         } catch (Exception e) { 37             // TODO: handle exception 38         }         39         return null; 40     } 41      42 }  復制代碼 核心代碼是第29行和第31行。   (4)在MainActicity中實例化:即實例化需要訪問的鏈接path和需要解析的節點nodeName   布局界面很簡單,只有一個按鈕控件,這裡就不展示布局代碼了。點擊按鈕後,觸發點擊事件,因為是Android4.0+,所以不能在主線程中訪問網絡,需要另起一個線程,這裡使用Thread類。代碼如下:    復制代碼  1 package com.example.androidsaxxml;  2   3 import java.io.InputStream;  4 import java.util.HashMap;  5 import java.util.List;  6   7 import android.app.Activity;  8 import android.os.Bundle;  9 import android.view.View; 10 import android.widget.Button; 11  12 import com.example.androidsaxxml.http.HttpUtils; 13 import com.example.androidsaxxml.service.SaxService; 14  15  16 public class MainActivity extends Activity { 17     private Button button; 18     @Override 19     protected void onCreate(Bundle savedInstanceState) { 20         super.onCreate(savedInstanceState); 21         setContentView(R.layout.activity_main); 22          23         button=(Button)findViewById(R.id.button1); 24         button.setOnClickListener(new View.OnClickListener() { 25              26             @Override 27             //點擊按鈕,開啟線程訪問網絡 28             public void onClick(View v) { 29                 Thread thread=new Thread(new Runnable() { 30                      31                     @Override 32                     public void run() { 33                         // 設置XML文檔的路徑 34                         String path="http://192.168.1.112:8080/smyhvae.xml"; 35                         //調用類HttpUtils:從服務器上獲取XML流。 36                         InputStream inputStream=HttpUtils.getXML(path); 37                         try { 38                             //調用類SaxService:解析流,同時設定需要解析的節點 39                             List<HashMap<String, String>> list=SaxService.readXML(inputStream, "person"); 40                             for(HashMap<String,String> map:list) 41                             { 42                                 //打印到LogCat中 43                                 System.out.println(map.toString()); 44                             } 45                         } catch (Exception e) { 46                             // TODO: handle exception 47                         } 48                     } 49                 }); 50                 thread.start();                 51             } 52         });         53     } 54 }
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved