一、三种解析的定义。
1.DOM解析
DOM意为Document Object Model,是文件对象模型,在解析XML文件的时候,会把整个文件加载到内存。
2.SAX解析
SAX意为 Simple API For XML,是事件驱动模型,在解析的过程中是一步一步的解析,不需要把整个文件加载到内存中。
3.Pull解析
Pull解析是Android官方推荐使用的一种解析方式,本身和Sax解析很相似。
二、三种解析方式的区别,优势劣势。
DOM解析
优势是可以在解析完成后,还可以倒着回退回去解析,因为文件被加载到内存中,无论要解析哪一个节点都是可以的。
劣势就是解析的时候需要把整个文档加载到内存,从而占用空间,资源消耗很大。
SAX解析
优势在于解析速度快,不必要把整个文档加载到内存里,消耗会比较小,效率比较高
劣势在于只能够按照顺序解析下来,倒着回退解析是没有办法的。
Pull解析
相对于Sax解析的区别在于,貌似是。。。。Sax解析不可以随时停止,要完全解析完成,pull是在while循环解析的,那么可以自由的添加在什么时候解析完成。
SAX解析器的工作方式是自动将事件推入注册的事件处理器进行处理,因此你不能控制事件的处理主动结束;而Pull解析器的工作方式为允许你的应用程序代码主动从解析器中获取事件,正因为是主动获取事件,因此可以在满足了需要的条件后不再获取事件,结束解析。这是他们主要的区别。[1]
三、三种解析的代码实现。
XML文件
<?xml version="1.0" encoding="UTF-8"?>
<books>
<book id="1">
<name>Developer</name>
<price>20.0</price>
</book>
<book id="2" >
<name>Engineer</name>
<price>30.0</price>
</book>
</books>
Java代码
package cn.My.utils;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import cn.My.Model.Books;
public class Parser {
/**
* DOM解析
*
* @param is
* @return List<Books>
*/
public List<Books> DOM_Parser(InputStream is) {
List<Books> data = new ArrayList<Books>();
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(is);//db要解析的文件流
Element root = doc.getDocumentElement();//获取根节点。
NodeList nodeList = root.getElementsByTagName("book");//根节点获取book节点列表
for (int i = 0; i < nodeList.getLength(); i++) {
Books book = new Books();
Element e = (Element) nodeList.item(i);//每一本书的节点。
book.bookId = Integer.valueOf(e.getAttribute("id"));//获取book属性
book.bookName = e.getElementsByTagName("name").item(0)
.getFirstChild().getNodeValue();//获取节点名为name的节点,并且getFirstChild,得到文本节点,getNodeValue是文本节点的值,也就是name的值。(name)
book.bookPrice = Float.valueOf(e.getElementsByTagName("price")
.item(0).getFirstChild().getNodeValue());
data.add(book);
}
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return data;
}
/**
* SAX解析
* @param is
* @return
*/
public List<Books> Sax_Parser(InputStream is) {
SAXParserFactory sf = SAXParserFactory.newInstance();
MyHandler myHandler = new MyHandler();
try {
SAXParser sp = sf.newSAXParser();
sp.parse(is, myHandler);
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
}
return myHandler.getData();
}
/**
* pull解析
* @param is
* @return List<Books>
*/
public List<Books> Pull_Parser(InputStream is){
List<Books> data =null;
Books book = null;
try {
XmlPullParserFactory xppf = XmlPullParserFactory.newInstance();
XmlPullParser xpp = xppf.newPullParser();
xpp.setInput(is, "UTF-8");
int event = xpp.getEventType();
while (event!=XmlPullParser.END_DOCUMENT) {
switch (event) {
case XmlPullParser.START_DOCUMENT:
data = new ArrayList<Books>();
break;
case XmlPullParser.START_TAG:
String str = xpp.getName();
if("book".equals(str)){
book = new Books();
book.bookId = Integer.valueOf(xpp.getAttributeValue(0));
}
if("name".equals(str)){
book.bookName = xpp.nextText();
}
if("price".equals(str)){
book.bookPrice = Float.valueOf(xpp.nextText());
}
break;
case XmlPullParser.END_TAG:
if("book".equals(xpp.getName())){
data.add(book);
book =null;
}
break;
}
event = xpp.next();
}
} catch (XmlPullParserException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NumberFormatException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return data;
}
public class MyHandler extends DefaultHandler {
private List<Books> data;
private Books book;
private String tarName;
@Override
public void startDocument() throws SAXException {
super.startDocument();
data = new ArrayList<Books>();
}
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
super.startElement(uri, localName, qName, attributes);
if ("book".equals(localName)) {
book = new Books();
book.bookId = Integer.valueOf(attributes.getValue(0));
}
tarName = localName;
}
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
super.characters(ch, start, length);
if ("name".equals(tarName)) {
book.bookName = new String(ch, start, length);
}
if ("price".equals(tarName)) {
book.bookPrice = Float.valueOf(new String(ch, start, length));
}
tarName=null;
}
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
if("book".equals(localName)){
data.add(book);
book = null;
}
}
@Override
public void endDocument() throws SAXException {
super.endDocument();
}
public List<Books> getData() {
return data;
}
}
}
分析:
DOM解析,把InputStream写到Document里面,通过doc.getDocumentElement();获取根节点,也就是解析到了<books>的时候,
再通过root.getElementByTagName("book");获得所有节点名为book的节点,返回一个nodelist的对象,也就是说,有多少的book,nodeList的长度就是多少,
所以,在这个时候新建book对象是非常合适的。接下来就是是对每一个book节点对象的继续获取元素节点,即name,price。
当然在获取到值之后都封装在book对象里面,然后在循环里面把book添加到data,返回即可。
SAX解析,首先要集成一个DefaultHandler的类,因为整个的解析都是在这个类完成的。
要重写的方法是
startDocument()
startElement(String uri, String localName, String qName,Attributes attributes)
characters(char[] ch, int start, int length)
endElement(String uri, String localName, String qName)
这4个即可。
startDocument()就是开始解析文档,这时候,就可以初始化一个List<Books>的列表。因为接下去要在解析过程中要去添加,所以要在这边做初始化。
startElement,对每一个元素节点进行解析,其中参数localName就是节点名儿,我们需要在开始的时候就建立一个String tagName用来保存localName的值
当localName的值和"book"一致,那么这时候就说明解析到了一本书,这时候就可以新建一个book对象了,然后把localname保存在tagname。把bookId的值,用attribute去获得即可。
characters就是解析到的文本节点的内容了,刚刚保存的tagName在这时候进行和“name”,"price"进行比较,如果一致的话,那么就可以把bookName,bookPrice的值封装起来了。并且这时候要把tagname赋值为null,否则如果不为空的时候,第二次到了空白的元素节点,tagName不为空,又会进行一次-判断,并且把得到的空白元素节点的值付给了bookName和bookPrice,也就得到了错误的解析结果
endElement,判断,如果tagName的值是book,那么就代表解析一本book,也就要把book添加到data里面去。然后把book对象赋值为空。
Pull解析
比Sax解析爽的一点就是不用写这个啥子Handler....其实也差不多- -、
要获得xpp对象,并且要给予流的来源
xpp.SetInput(is,"UTF-8");
这时候需要一个int型的event用来接受节点状态的,此时把xpp.getEventType获得到,在While循环里开始解析
只要event != END_DOCUMENT,那就一直解析。是不是很好理解?
这时候switch用来判断,
如果 event==StartDocument,那么就新建一个List<Books> 的对象,用于添加book
如果 event == StartElement ,那么在嵌套if判断
getName的值等于book,就new一个book对象,值等于name,就把getNextText的值赋值给book.bookName.同理,price也是一样。
如果event == EndElement ,加if判断,如果getName的值是book,那么说明就解析完了一本书,这时候就要把book添加到data里面,并且赋值为空。
最后,要记得移动xpp,要不怎么一直往下解析呢?
所以 event = xpp.next();
附:
关于节点问题
从book开始解析有5个元素节点,book一个,黑色线条的空白是第二个,name整个算时候第三个,第二条黑色线条的空白是第四个,price是第五个
而被夹在name和/name之间的也是一个节点,叫做文本节点,所以这也就是在获取到了name节点之后,还要getFirstChild的原因,获取到了FirstChild才是获得到了文字节点,再getNodeValue才会得到正确的值。
大概就是这么多把,作为自己学习完成的笔记,总结才会有更多的收获,可能语言组织上有点混乱什么的,渣渣文笔,如果文中有什么问题,或者是有什么好的建议和意见,请留言给我,谢谢。
PS:红色字体为网络Copy.....
[1] 来源http://blog.csdn.net/leorowe/article/details/6841375