一、緒論
上周工作需要了解項目的一些大體内容,結果在xml解析這一塊看的迷迷糊糊的,是以在這裡把學習到xml解析的一些知識記錄一下。
二、分析
android中的xml解析器主要有三種,DOM解析器、SAX解析器和pull解析器。
1、DOM解析器
DOM(Document Object Model) 是一種用于XML文檔的對象模型,可用于直接通路XML文檔的各個部分。它是一次性全部将内容加載在記憶體中,生成一個樹狀結構,它沒有涉及回調和複雜的狀态管理。 缺點是加載大文檔時效率低下,是以一般在解析大文檔時不建議使用DOM解析。
分析該結構通常需要加載整個文檔和構造樹形結構,然後才可以檢索和更新節點資訊。Android完全支援DOM 解析。利用DOM中的對象,可以對XML文檔進行讀取、搜尋、修改、添加和删除等操作。
DOM的工作原理:使用DOM對XML檔案進行操作時,首先要解析檔案,将檔案分為獨立的元素、屬性和注釋等,然後以節點樹的形式在記憶體中對XML檔案進行表示,就可以通過節點樹通路文檔的内容,并根據需要修改文檔。
常用的DOM的接口和類:
Document:該接口定義分析并建立DOM文檔的一系列方法,它是文檔樹的根,是操作DOM的基礎。
Node:該接口提供處理并擷取節點和子節點值的方法。
Element:該接口繼承Node接口,提供了擷取、修改XML元素名字和屬性的方法。
NodeList:提供獲得節點個數和目前節點的方法。這樣就可以疊代地通路各個節點。
DOMParser:該類是Apache的Xerces中的DOM解析器類,可直接解析XML檔案。
2、SAX解析
SAX(Simple API for XML) 使用流式處理的方式,它并不記錄所讀内容的相關資訊。它是一種以事件為驅動的XML API,解析速度快,占用記憶體少。使用回調函數來實作。 缺點是因為以事件為驅動的它不能回退。
它的核心是事件處理模式,主要是圍繞着事件源以及事件處理器來工作的。當事件源産生事件後,調用事件處理器相應的處理方法,一個事件就可以得到處理。在事件源調用事件處理器中特定方法的時候,還要傳遞給事件處理器相應事件的狀态資訊,這樣事件處理器才能夠根據提供的事件資訊來決定自己的行為。
SAX的工作原理:SAX會順序掃描文檔,在掃描到文檔(document)開始與結束、元素(element)開始與結束、元素内容(characters)等時通知事件處理方法,事件處理方法進行相應處理,然後繼續掃描,指導文檔掃描結束。
常用的SAX接口和類:
Attrbutes:用于得到屬性的個數、名字和值。
ContentHandler:定義與文檔本身關聯的事件(例如,開始和結束标記)。大多數應用程式都注冊這些事件。
DTDHandler:定義與DTD關聯的事件。它沒有定義足夠的事件來完整地報告DTD。如果需要對DTD進行文法分析,請使用可選的DeclHandler。
DeclHandler是SAX的擴充。不是所有的文法分析器都支援它。
EntityResolver:定義與裝入實體關聯的事件。隻有少數幾個應用程式注冊這些事件。
ErrorHandler:定義錯誤事件。許多應用程式注冊這些事件以便用它們自己的方式報錯。
DefaultHandler:它提供了這些接LI的預設實作。在大多數情況下,為應用程式擴充DefaultHandler并覆寫相關的方法要比直接實作一個接口更容易。
下面是部分說明:
SAX處理器說明
部分常用方法說明
是以,我們通常要使用XmlReader和DefaultHandler配合起來解析xml文檔。
SAX的解析流程:
startDocument --> startElement --> characters --> endElement --> endDocument
3、pull解析
Pull内置于Android系統中。也是官方解析布局檔案所使用的方式。Pull與SAX有點類似,都提供了類似的事件,如開始元素和結束元素。不同的是,SAX的事件驅動是回調相應方法,需要提供回調的方法,而後在SAX内部自動調用相應的方法。而Pull解析器并沒有強制要求提供觸發的方法。因為他觸發的事件不是一個方法,而是一個數字。它使用友善,效率高。Android官方推薦開發者們使用Pull解析技術。Pull解析技術是第三方開發的開源技術,它同樣可以應用于JavaSE開發。
pull傳回的常量:
讀取到xml的聲明傳回 START_DOCUMENT;
讀取到xml的結束傳回 END_DOCUMENT ;
讀取到xml的開始标簽傳回 START_TAG;
讀取到xml的結束标簽傳回 END_TAG;
讀取到xml的文本傳回 TEXT;
pull的工作原理:pull提供了開始元素和結束元素。當某個元素開始時,我們可以調用parser.nextText從XML文檔中提取所有字元資料。當解釋到一個文檔結束時,自動生成EndDocument事件。
常用的XML pull的接口和類:
XmlPullParser:XML pull解析器是一個在XMLPULL VlAP1中提供了定義解析功能的接口。
XmlSerializer:它是一個接口,定義了XML資訊集的序列。
XmlPullParserFactory:這個類用于在XMPULL V1 API中建立XML Pull解析器。
XmlPullParserException:抛出單一的XML pull解析器相關的錯誤。
pull的解析流程:
start_document --> end_document --> start_tag -->end_tag
在Android中還有第四種方式:android.util.Xml類(本人未使用過)
在Android API中,另外提供了Android.util.Xml類,同樣可以解析XML檔案,使用方法類似SAX,也都需編寫Handler來處理XML的解析,但是在使用上卻比SAX來得簡單 ,如下所示:
以android.util.XML實作XML解析 :
MyHandler myHandler=new MyHandler0;
android.util.Xm1.parse(url.openC0nnection().getlnputStream(),Xml.Encoding.UTF-8,myHandler);
三、實踐
1、首先建立一個參考xml文檔 (放在了assets目錄中)
靈渠在廣西壯族自治區興安縣境内,是世界上最古老的運河之一,有着“世界古代水利建築明珠”的美譽。靈渠古稱秦鑿渠、零渠、陡河、興安運河,于公元前214年鑿成通航,距今已2217年,仍然發揮着功用。
http://imgsrc.baidu.com/baike/pic/item/389aa8fdb7b8322e08244d3c.jpg
膠萊運河南起黃海靈山海口,北抵渤海三山島,流經現膠南、膠州、平度、高密、昌邑和萊州等,全長200公裡,流域面積達5400平方公裡,南北貫穿山東半島,溝通黃渤兩海。膠萊運河自平度姚家村東的分水嶺南北分流。南流由麻灣口入膠州灣,為南膠萊河,長30公裡。北流由海倉口入萊州灣,為北膠萊河,長100餘公裡。
http://imgsrc.baidu.com/baike/pic/item/389aa8fdb7b8322e08244d3c.jpg
位于淮河下遊江蘇省北部,西起洪澤湖邊的高良澗,流經洪澤,青浦、淮安,阜甯、射陽,濱海等六縣(區),東至扁擔港口入海的大型人工河道。全長168km。
http://imgsrc.baidu.com/baike/pic/item/389aa8fdb7b8322e08244d3c.jpg
我們需要用一個River對象來儲存資料,友善觀察節點資訊,抽象出River類public class River {
String name;// 名稱
Integer length;// 長度
String introduction;// 介紹
String Imageurl;// 圖檔url
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getLength() {
return length;
}
public void setLength(Integer length) {
this.length = length;
}
public String getIntroduction() {
return introduction;
}
public void setIntroduction(String introduction) {
this.introduction = introduction;
}
public String getImageurl() {
return Imageurl;
}
public void setImageurl(String imageurl) {
Imageurl = imageurl;
}
@Override
public String toString() {
return "River [name=" + name + ", length=" + length + ", introduction="
+ introduction + ", Imageurl=" + Imageurl + "]";
}
}
采用DOM解析時具體處理步驟是:
1 首先利用DocumentBuilderFactory建立一個DocumentBuilderFactory執行個體
2 然後利用DocumentBuilderFactory建立DocumentBuilder
3 然後加載XML文檔(Document),
4 然後擷取文檔的根結點(Element),
5 然後擷取根結點中所有子節點的清單(NodeList),
6 然後使用再擷取子節點清單中的需要讀取的結點。
下面我們就開始讀取xml文檔對象,并添加進List中:
代碼如下: 我們這裡是使用assets中的river.xml檔案,那麼就需要讀取這個xml檔案,傳回輸入流。 讀取方法為:inputStream=this.context.getResources().getAssets().open(fileName); 參數是xml檔案路徑,當然預設的是assets目錄為根目錄。
然後可以用DocumentBuilder對象的parse方法解析輸入流,并傳回document對象,然後再周遊doument對象的節點屬性。
private ListDOMfromXML(String filePath) {
ArrayListlist = new ArrayList();
DocumentBuilderFactory factory = null;
DocumentBuilder builder = null;
Document document = null;
InputStream inputStream = null;
//建構解析器
factory = DocumentBuilderFactory.newInstance();
try {
builder = factory.newDocumentBuilder();
//找到xml檔案并且加載
inputStream = this.getResources().getAssets().open(filePath);//getAssets後預設根目錄為assets
document = builder.parse(inputStream);
//找到根Element
Element root=document.getDocumentElement();
NodeList nodes=root.getElementsByTagName(RIVER);
//周遊根節點所有子節點,rivers 下所有river
River river = null;
for (int i = 0; i < nodes.getLength(); i++) {
river = new River();
//擷取river元素節點
Element riverElement = (Element) nodes.item(i);
//設定river中name和length屬性值
river.setName(riverElement.getAttribute("name"));
river.setLength(Integer.parseInt(riverElement.getAttribute("length")));
//擷取子标簽
Element introduction = (Element) riverElement.getElementsByTagName(INTRODUCTION).item(0);
Element imageurl = (Element) riverElement.getElementsByTagName(IMAGEURL).item(0);
//設定introduction和imageurl屬性
river.setIntroduction(introduction.getFirstChild().getNodeValue());
river.setImageurl(imageurl.getFirstChild().getNodeValue());
list.add(river);
}
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
for (River river : list) {
Log.w("DOM Test", river.toString());
}
return list;
}
在這裡添加到List中, 然後我們使用log将他們列印出來。如圖所示:
XML解析結果
采用SAX解析時具體處理步驟是:
1 建立SAXParserFactory對象
2 根據SAXParserFactory.newSAXParser()方法傳回一個SAXParser解析器
3 根據SAXParser解析器擷取事件源對象XMLReader
4 執行個體化一個DefaultHandler對象
5 連接配接事件源對象XMLReader到事件處理類DefaultHandler中
6 調用XMLReader的parse方法從輸入源中擷取到的xml資料
7 通過DefaultHandler傳回我們需要的資料集合。
代碼如下:
private ListSAXfromXML(String filePath) {
ArrayListlist = new ArrayList();
//建構解析器
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = null;
XMLReader xReader = null;
try {
parser = factory.newSAXParser();
//擷取資料源
xReader = parser.getXMLReader();
//設定處理器
RiverHandler handler = new RiverHandler();
xReader.setContentHandler(handler);
//解析xml檔案
xReader.parse(new InputSource(this.getAssets().open(filePath)));
list = handler.getList();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
for (River river : list) {
Log.w("DOM Test", river.toString());
}
return list;
}