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();