android XML解析學習——Dom方式

一.基礎知識
  在《OPhone XML解析學習--Sax方式》和《OPhone XML解析學習--Sax方式(續)》中我們學習了在OPhone/Android平臺上使用SAX的方式解析XML文檔的方法。除了SAX的方式,還有一種常用的爲DOM(Document Object Model文檔對象模型)方式。
  DOM方式解析XML的過程爲首先將所有 XML 文檔讀取到內存中,構造一個樹結構,然後允許使用 DOM API 來操作整個樹結構,包括遍歷 XML 樹、檢索所需的數據等。
  OPhone/Android平臺使用Java SDK中的API來實現DOM方式的解析,具體的方法和之前學習的使用Java SDK中的SAX方式解析XML類似,因此有了前面SAX的基礎,學習DOM方式就會比較簡單了。DOM 和 之前的SAX 的主要差別是 API 本身的結構,SAX 由基於事件的回調集組成,邊解析邊產生事件從而調用事件處理器的回調函數進行處理。而DOM不是,DOM直接讀取整個XML樹保存爲Document類,因此只需處理保存在內存中的Document對象即可,沒有回調函數。
  DOM解析相關的包爲javax.xml.parsers和org.w3c.dom。其中javax.xml.parsers提供Document構造器工廠DocumentBuilderFactory和Document構造器DocumentBuilder,DocumentBuilderFactory實例調用newDocumentBuilder()創建構造器DocumentBuilder實例,然後該實例就可以調用parse方法來返回一個解析爲Document類的實例。Document 類由表示XML文檔的包含元素、屬性、文本內容等的各個節點組成。因此有了Document對象後就可以按需要獲取xml文檔中各個子節點及其相關的值了。Document、Element、Node等各個和DOM相關的具體部分都在org.w3c.dom包中,因此和SAX類似,org.w3c.dom包是底層具體的負責DOM解析相關的內容,並且爲上層javax.xml.parsers包提供DOM解析器等相關調用。

  下面我們就用上面介紹的DOM方式來實現解析XML形式的USGS地震數據的Demo例子。

  二.實例開發
  我們要完成的效果圖如下圖1所示:

  圖1 ListView列表顯示的地震數據

  和上一部分Demo例子的一樣,也是解析完地震數據後用ListView列表的方式顯示每條地震的震級和地名信息。
  新建一個OPhone工程OPhoneXMLDemoDom。
  要添加的基本內容和上一個Demo中的一樣,這裏就不再贅述,這次要添加的解析器新類爲DomEarthquakeHandler,內容如下所示:

 

  1. public class DomEarthquakeHandler {  
  2.    //xml解析用到的Tag  
  3.    private String kEntryElementName = "entry";  
  4.    private String kLinkElementName = "link";  
  5.    private String kLinkAttributeName = "href";  
  6.    private String kTitleElementName = "title";  
  7.    private String kUpdatedElementName = "updated";  
  8.    private String kGeoRSSPointElementName = "georss:point";  
  9.    private String kGeoRSSElevElementName = "georss:elev";  
  10.    //用於保存xml解析獲取的結果  
  11.    private ArrayList<EarthquakeEntry> earthquakeEntryList;  
  12.    private EarthquakeEntry earthquakeEntry = null;  
  13.    //解析xml數據  
  14.    public ArrayList<EarthquakeEntry> parse(InputStream inStream)  
  15.    {  
  16.     earthquakeEntryList = new ArrayList<EarthquakeEntry>();  
  17.     //創建DocumentBuilder  
  18.     DocumentBuilderFactory docBFactory = DocumentBuilderFactory.newInstance();  
  19.     DocumentBuilder docBuilder = null;  
  20.     try {  
  21.      docBuilder = docBFactory.newDocumentBuilder();  
  22.     } catch (ParserConfigurationException e1) {  
  23.      // TODO Auto-generated catch block  
  24.      e1.printStackTrace();  
  25.     }   
  26.     //解析地震數據流.  
  27.     Document doc = null;  
  28.     try {  
  29.      doc = docBuilder.parse(inStream);  
  30.     } catch (SAXException e1) {  
  31.      // TODO Auto-generated catch block  
  32.      e1.printStackTrace();  
  33.     } catch (IOException e1) {  
  34.      // TODO Auto-generated catch block  
  35.      e1.printStackTrace();  
  36.     }  
  37.     Element docEle = doc.getDocumentElement();  
  38.       
  39.     // Get a list of each earthquake entry.  
  40.     NodeList nList = docEle.getElementsByTagName(kEntryElementName);  
  41.     if(nList != null && nList.getLength() > 0)  
  42.     {  
  43.      for(int i=0; i < nList.getLength(); i++)  
  44.      {  
  45.       try {  
  46.        Element entry = (Element)nList.item(i);  
  47.        Element title = (Element)entry.getElementsByTagName(kTitleElementName).item(0);  
  48.        Element when = (Element)entry.getElementsByTagName(kUpdatedElementName).item(0);  
  49.        Element gpoint = (Element)entry.getElementsByTagName(kGeoRSSPointElementName).item(0);  
  50.        Element gelev = (Element)entry.getElementsByTagName(kGeoRSSElevElementName).item(0);  
  51.        Element link = (Element)entry.getElementsByTagName(kLinkElementName).item(0);  
  52.          
  53.        String details = title.getFirstChild().getNodeValue();  
  54.        String point = gpoint.getFirstChild().getNodeValue();  
  55.        String elevS = gelev.getFirstChild().getNodeValue();  
  56.        String date = when.getFirstChild().getNodeValue();  
  57.        //獲取link鏈接  
  58.        String webLink = link.getAttribute(kLinkAttributeName);  
  59.    //    Log.v("Dom", webLink);  
  60.        //構造更新時間  
  61.        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'");  
  62.        Date qdate = new GregorianCalendar(0,0,0).getTime();  
  63.              try {  
  64.             qdate = sdf.parse(date);  
  65.           } catch (ParseException e) {  
  66.             e.printStackTrace();  
  67.           }  
  68.        //提取經緯度信息  
  69.           String[] latLongitude = point.split(" ");  
  70.           Location location = new Location("dummyGPS");  
  71.           location.setLatitude(Double.parseDouble(latLongitude[0]));  
  72.           location.setLongitude(Double.parseDouble(latLongitude[1]));  
  73.           //提取強度信息  
  74.           String magnitudeString = details.split(" ")[1];  
  75.           int end =  magnitudeString.length()-1;  
  76.           double magnitude = Double.parseDouble(magnitudeString.substring(0, end));  
  77.           //提取位置信息  
  78.           String place = details.split(",")[1].trim();  
  79.           //提取海拔高度信息  
  80.           double evel;  
  81.           //因爲USGS數據有可能會輸錯,比如爲"--10000",多了一個"-"號  
  82.           try {  
  83.            evel = Double.parseDouble(elevS);  
  84.        } catch (Exception e) {  
  85.         // TODO: handle exception  
  86.         e.printStackTrace();  
  87.         evel = 0;  
  88.        }  
  89.           Log.v("Dom_Elev", String.valueOf(evel));          
  90.           earthquakeEntry = new EarthquakeEntry(qdate, place, webLink, location, magnitude, evel);  
  91.             
  92.           earthquakeEntryList.add(earthquakeEntry);   
  93.       } catch (Exception e) {  
  94.        // TODO: handle exception  
  95.        e.printStackTrace();  
  96.       }  
  97.      }  
  98.     }  
  99.     Log.v("Dom""End");  
  100.     return earthquakeEntryList;  
  101.    }  
  102.   }  

  程序首先也是定義解析時需要用到的元素Tag名稱變量,其中因爲DOM沒有像SAX方式那樣可以把一個元素的的名稱分開爲表示名稱空間的Uri和當前標籤名的localname,只是通過getElementsByTagName方法由完整的名稱讀取,因此需要定義完整的元素標籤名稱:

  1. private String kGeoRSSPointElementName = "georss:point";  
  2.   private String kGeoRSSElevElementName = "georss:elev";  


  在定義的用於解析xml數據的方法中

  1. public ArrayList<EarthquakeEntry> parse(InputStream inStream)  

  
  使用工廠類創建Document構造器DocumentBuilder:

  1. //創建DocumentBuilder  
  2.    DocumentBuilderFactory docBFactory = DocumentBuilderFactory.newInstance();  
  3.    DocumentBuilder docBuilder = null;  
  4.    try {  
  5.     docBuilder = docBFactory.newDocumentBuilder();  
  6.    } catch (ParserConfigurationException e1) {  
  7.     // TODO Auto-generated catch block  
  8.     e1.printStackTrace();  
  9.    }   


   然後調用DocumentBuilder實例的parse方法解析xml文檔數據並返回Document對象,並通過getDocumentElement()方法獲取文檔的根元素。

  1. //解析地震數據流.  
  2.   Document doc = null;  
  3.   try {  
  4.    doc = docBuilder.parse(inStream);  
  5.   } catch (SAXException e1) {  
  6.    // TODO Auto-generated catch block  
  7.    e1.printStackTrace();  
  8.   } catch (IOException e1) {  
  9.    // TODO Auto-generated catch block  
  10.    e1.printStackTrace();  
  11.   }  
  12.   Element docEle = doc.getDocumentElement();  


  有了XML文檔的根元素,就可以通過getElementsByTagName等方法獲取具體的子元素,及子節點和節點值等。具體方法可以參考以上的代碼。
  最後添加OPhoneXMLDemoDom.java文件中的內容,內容和前一個Demo工程OPhoneXMLDemoSaxII中的OPhoneXMLDemoSaxII.java基本一樣,

  1. public class OPhoneXMLDemoDom extends Activity {  
  2.   /** Called when the activity is first created. */  
  3.    //定義顯示的List相關變量  
  4.    ListView list;  
  5.    ArrayAdapter<EarthquakeEntry> adapter;  
  6.    ArrayList<EarthquakeEntry> earthquakeEntryList;  
  7.   @Override  
  8.   public void onCreate(Bundle savedInstanceState) {  
  9.   super.onCreate(savedInstanceState);  
  10.   setContentView(R.layout.main);  
  11.   
  12.   //獲取地震數據流  
  13.   InputStream earthquakeStream = readEarthquakeDataFromFile();  
  14.   //Dom方式進行xml解析  
  15.   DomEarthquakeHandler domHandler = new DomEarthquakeHandler();  
  16.   earthquakeEntryList = domHandler.parse(earthquakeStream);  
  17.     //用ListView進行顯示  
  18.     list = (ListView)this.findViewById(R.id.list);  
  19.     adapter = new ArrayAdapter<EarthquakeEntry>(this, android.R.layout.simple_list_item_1, earthquakeEntryList);  
  20.     list.setAdapter(adapter);  
  21.   }  
  22.   
  23.   private InputStream readEarthquakeDataFromFile()  
  24.   {  
  25.     //從本地獲取地震數據  
  26.     InputStream inStream = null;  
  27.     try {  
  28.      inStream = this.getAssets().open("USGS_Earthquake_1M2_5.xml");  
  29.     } catch (IOException e) {  
  30.      // TODO Auto-generated catch block  
  31.      e.printStackTrace();  
  32.     }  
  33.     return inStream;  
  34.   }  
  35.   private InputStream readEarthquakeDataFromInternet()  
  36.   {  
  37.    //從網絡上獲取實時地震數據  
  38.    URL infoUrl = null;  
  39.    InputStream inStream = null;  
  40.    try {  
  41.      infoUrl = new URL("http://earthquake.usgs.gov/earthquakes/catalogs/1day-M2.5.xml");  
  42.      URLConnection connection = infoUrl.openConnection();  
  43.      HttpURLConnection httpConnection = (HttpURLConnection)connection;  
  44.      int responseCode = httpConnection.getResponseCode();  
  45.      if(responseCode == HttpURLConnection.HTTP_OK)  
  46.      {  
  47.       inStream = httpConnection.getInputStream();  
  48.      }  
  49.     } catch (MalformedURLException e) {  
  50.      // TODO Auto-generated catch block  
  51.      e.printStackTrace();  
  52.     } catch (IOException e) {  
  53.      // TODO Auto-generated catch block  
  54.      e.printStackTrace();  
  55.     }  
  56.     return inStream;  
  57.   }  
  58.   }  
  59.   只是把進行XML解析的部分換成了如下方式:  
  60.   //Dom方式進行xml解析  
  61.   DomEarthquakeHandler domHandler = new DomEarthquakeHandler();  
  62.   earthquakeEntryList = domHandler.parse(earthquakeStream);  

  完成了,可以保存運行看下效果。

  這樣我們就已經學習了OPhone/Android上使用SAX和DOM兩種方式解析XML的方法,下面我們簡單總結比較一下兩者的區別。
  DOM解析器是通過將XML文檔解析成樹狀模型並將其放入內存來完成解析工作的,而後對文檔的操作都是在這個樹狀模型上完成的。這個在內存中的文檔樹將是 文檔實際大小的幾倍。這樣做的好處是結構清除、操作方便,而帶來的麻煩就是極其耗費系統資源。而SAX正好克服了DOM的缺點。SAX解析器的處理過程是 通讀整個文檔,根據文檔內容產生事件,而把對這些事件的處理交由事件處理器處理。SAX不需要在內存中保存整個文檔,它對系統資源的節省是顯而易見的。這 樣在一些需要處理大型XML文檔和性能要求比較高的場合就要用SAX了。
  我們以表格的形式看一下他們在一些方面的比較:

 

SAX
DOM
順序讀入文檔併產生相應事件,可以處理任何大小的XML文檔
在內存中創建文檔樹,不適於處理大型XML文檔
只能對文檔按順序解析一遍,不支持對文檔的隨意訪問
可以隨意訪問文檔樹的任何部分,沒有次數限制
只能讀取XML文檔內容,而不能修改
可以隨意修改文檔樹,從而修改XML文檔
開發上比較複雜,需要自己來實現事件處理器
易於理解,易於開發

 

  通過比較我們可以知道SAX和DOM各自適合的場合:
  SAX適於處理下面的問題:
  1. 對大型文檔進行處理
  2. 只需要文檔的部分內容,或者只需要從文檔中得到特定信息
  DOM適合處理下面的問題:
  1. 需要對文檔進行修改
  2. 需要隨機對文檔進行訪問
  以上只是對SAX和DOM方式傳統的比較,而對具體的在OPhone/Android平臺上使用來說,因爲DOM更加佔用內存,而對運行在移動設備上的OPhone/Android系統來說往往內存是一個比較稀缺的資源,因此除非XML 文檔始終保持很小的大小,否則相對來說推薦在OPhone/Android 應用程序中使用SAX 方式解析XML 。並且OPhone/Android系統爲SAX還提供了額外的實用工具android.util.Xml和特有的包android.sax等,因此實際上在OPhone/Android使用SAX也一樣很方便了。

  三.總結
  在這部分中我們學習了使用DOM方式解析XML的方法,並且比較了DOM方式和SAX方式的各種特點及區別。
  從以上的學習我們知道OPhone/Android平臺已經爲XML的解析提供了強大的功能,既可以使用Java SDK中的SAX和DOM方式來解析,還可以使用OPhone/Android SDK提供的SAX方式。但OPhone/Android平臺上還有一種解析XML的方式,就是使用Pull解析器,這部分內容我們以後接着學習。

  注:
  參考資料:http://www.ibm.com/developerworks/cn/xml/x-jaxp/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章