天天看點

XML解析的三種方式總結

XML解析有三種方式,這裡來總結一下

*************************SAX*************************

首先是SAX方式,這種方式是邊加載邊解析

關鍵的一個類是DefaultHandler,我們通過內建這個類,Override一些關鍵的方法,進而解析XML檔案

主要的方法有

@Override
	public void startDocument() throws SAXException {
		list = new ArrayList<Person>();
		Log.i(tag,"startDocument()");
	}
           

這個方法是在開始解析文檔的時候調用,通常做一下初始化的工作,然後就緒執行startElement()方法

@Override
	public void startElement(String uri, String localName, String qName,
			Attributes attributes) throws SAXException {
......
preTag = localName;
 }
           

我在startElement()方法裡面解析XML标簽,其中localName是目前标簽的名字,attributes是存儲了标簽屬性的數組,我們通過if語句判斷是否我們需要的标簽,然後提取需要的屬性資訊來構造Bean就可以了

這裡我們使用一個preTag全局變量來存儲目前标簽,便于character方法的使用

然後會接着執行character()方法,我們在這個方法裡面提取文本資訊

@Override
	public void characters(char[] ch, int start, int length)
			throws SAXException {		
		String content = new String(ch,start,length);
.......
}
           

這裡構造出的content就是文本資訊了,但是我怎麼知道,這個那個标簽裡面的文本資訊,這時就要使用到preTag,來判斷是那個标簽

接着會調用endElement()方法

@Override
	public void endElement(String uri, String localName, String qName)
			throws SAXException {	
		.......
		Log.i(tag,localName+"***end element***");
		preTag = null;
	}
           

這裡我們有獲得了localName,也就是目前标簽的名字

并且我們有一個關鍵的操作,就是preTag=null的設定

因為xml檔案裡面,空白區域也被當成了節點,是以我們如果不設定pretag為空,那麼preTag記錄的就是上一個标簽的名字,當我們解析到空白節點時,進入character()方法,就可能把遠的preTag所對應的文本覆寫成空

最後調用endDocument()方法,做一些收尾的工作

@Override
	public void endDocument() throws SAXException {
		Log.i(tag,"endDocument()");
	}
           

繼承好DefaultHandler類以後,我就要使用

InputStream is = PersonServiceTest.class.getClassLoader().getResourceAsStream("person.xml");
		if(is!=null){
			SAXForHandler s = new SAXForHandler();
			SAXParserFactory pf = SAXParserFactory.newInstance();
			SAXParser sp = pf.newSAXParser();
			sp.parse(is,s);
			for(Person person:s.list){
				Log.i(Tag,person.toString());
			}
		}
           

使用方法很簡單,首先建立一個SAX工程,然後利用工程生成一個解析器

把xml資料流,和Handler對象傳入解析器就可以了

*************************DOM*************************

dom解析方式是先加載整個xml檔案再解析,這樣的好處是,我們知道了DOM樹的結構,和節點之間的關系,不利之處是要加載整個檔案,解析速度慢

解析方式如下,首先建立一個DOMbulider工程,然後生産一個DOMbulider,把xml資料流傳入DOMbulider.parse(),就可以生成一個DOMCUMENT對象

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder = factory.newDocumentBuilder();
		Document document = builder.parse(inStream);
           

下面的解析過程就比較難說清楚了,首先是擷取檔案根節點

Element root = document.getDocumentElement();
           

然後根據檔案根節點,擷取所有Node

NodeList personNodes = root.getElementsByTagName("person");
           

這裡要說明一下node跟Elment,我們主要使用element來擷取标簽的相關資訊,不過首先我們要使用Node轉換成Element

我們通過一個循環,來周遊所有的Person,用node.item()來擷取,對于每一個node,我們在強制轉換成Element

for(int i=0;i<personNodes.getLength();i++){
			Element personElement = (Element) personNodes.item(i);
.....
}
           

有了element對象,我就可以擷取它的屬性

int id = new Integer(personElement.getAttribute("id"));
           

接着像根節點一樣,擷取該element的所有子節點

NodeList childNodes = personElement.getChildNodes();
			for(int y=0;y<childNodes.getLength();y++){
.....
}
           

不斷地按照這個方式,就能完整構造出bean對象

*************************PULL*************************

PULL方法,可以說結合了上面兩種方法解析便利上的優點,并且它表示通過工廠建立的,而是直接建立,但是接下來把xml流傳進去解析,也是一樣的步驟

XmlPullParser pullParser = Xml.newPullParser();
		pullParser.setInput(inStream,"UTF-8");
           

下面的解析全都圍繞着pullParser這個對象進行了

首先一個方法是getEventType()擷取目前節點的類型,注意每次pullParser都表示目前正在解析的節點,隻有調用它的next()方法,才會移到下一個節點,類似cursor

判斷節點類型後,不是END_DOCUEMNT,我們就一直解析(next())

接着幾個屬性就是XmlPullParser.START_DOCUMENT,XmlPullParser.START_TAG,XmlPullParser.END_TAG

根據不同類型解析就可以了

pullParser.getName()獲得标簽名字

pullParser.getAttributeValue(0)獲得标簽屬性

pullParser.nextText()獲得标簽文本

調用上面的幾個方法,勾構造bean就可以了

int event = pullParser.getEventType();
		while(event!=XmlPullParser.END_DOCUMENT){
			switch(event){
			case XmlPullParser.START_DOCUMENT:
				persons = new ArrayList<Person>();
				break;
			case XmlPullParser.START_TAG:
				if("person".equals(pullParser.getName())){
					int id = new Integer(pullParser.getAttributeValue(0));
					person = new Person();
					person.setId(id);
				}
				if(person!=null){
					if("name".equals(pullParser.getName())){
						person.setName(pullParser.getAttributeValue(0));						
					}
					if("age".equals(pullParser.getName())){
						person.setAge(new Short(pullParser.nextText()));
					}
					if("ddd".equals(pullParser.getName())){						
						person.setDdd(pullParser.nextText());
					}
				}
				break;
			case XmlPullParser.END_TAG:
				if("person".equals(pullParser.getName())){
					persons.add(person);
					person = null;
				}
			}
			event = pullParser.next();
		}
           

說過XML的解析,我順便說一下xml檔案的構造,這種構造方法,跟pull解析的方式很類似,就是剛好反過來

首先擷取一個序列化構造對象,設定輸出流

然後startDocument()方法,就可以開始構造檔案根節點

然後startTag()就可以設定一個标簽

attribute()就設定這個标簽的屬性

text()就可以設定文本

endTag()就可以閉合标簽

其中startTag互相嵌套,就可以構成樹狀結構

最好endDocument()就可以關閉root了

XmlSerializer serializer = Xml.newSerializer();
		serializer.setOutput(outStream, "UTF-8");
		serializer.startDocument("UTF-8", true);
		serializer.startTag(null, "persons");
		for (Person person : persons) {
			serializer.startTag(null, "person");
			serializer.attribute(null, "id", "1");
			serializer.startTag(null, "name");
			serializer.text("zhangsan");
			serializer.endTag(null, "name");
		}
		serializer.endTag(null, "persons");
		serializer.endDocument();
		outStream.flush();
		outStream.close();
           

繼續閱讀