既然要介绍这三种解析方法那么当然要说一下为什么用他们,用他们有什么优点和缺点。
首先说一下Dom解析:
优点:易用性强,使用DOM时,将把所有的XML文档信息都存于内存中,并且遍历简单,支持XPath,增强了易用性。
缺点:效率低,解析速度慢,内存占用量过高,对于大文件来说几乎不可能使用。另外效率低还表现在大量的消耗时间,因为使用DOM进行解析时,将为文档的每个element、attribute、processing-instrUCtion和comment都创建一个对象,这样在DOM机制中所运用的大量对象的创建和销毁无疑会影响其效率。
接着说Sax解析:
优点:不用事先调入整个文档,占用资源少。尤其在嵌入式环境,如Android,极力推荐采用SAX进行解析。
缺点:不像DOM一样将文档树长期留驻在内存,数据不是长久的。事件过后,若没保存数据,那么数据就会丢失。
然后Pull解析:
优点:不必自己写事件逻辑,并且也不需要把文档读到内存。个人感觉优于以上两种解析方式。
缺点:对文档修改比较困难。
下面用代码来演示一下用法:
首先添加一下XML格式内容:
<apps>
<app>
<id>1</id>
<name>Google Map</name>
<version>1.0</version>
</app>
<app>
<id>2</id>
<name>Chrome</name>
<version>2.1</version>
</app>
<app>
<id>3</id>
<name>Google</name>
<version>2.3</version>
</app>
</apps>
Pull解析:
运行结果如下:
主要代码如下:
private void parseXMLWithPull(String responseData) {
try{
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
XmlPullParser xmlPullParser = factory.newPullParser();
xmlPullParser.setInput(new StringReader(responseData));
int evenType = xmlPullParser.getEventType();
String id = "";
String name = "";
String version = "";
while(evenType!=XmlPullParser.END_DOCUMENT){
String nodeName = xmlPullParser.getName();
switch (evenType){
//开始解析某个节点
case XmlPullParser.START_TAG:{
if ("id".equals(nodeName)){
id = xmlPullParser.nextText();
}else if ("name".equals(nodeName)){
name = xmlPullParser.nextText();
}else if ("version".equals(nodeName)){
version = xmlPullParser.nextText();
}break;
}
//解析完成某个节点
case XmlPullParser.END_TAG:{
if ("app".equals(nodeName)){
Log.d("MainActivity","id is"+ id);
Log.d("MainActivity","name is"+ name);
Log.d("MainActivity","Version is"+ version);
}
break;
}
default:
break;
}
evenType = xmlPullParser.next();
}
}catch (Exception e){
e.printStackTrace();
}
SAX解析:
首先要创建一个类继承ContentHandler,并重写父类的五个方法。
public class ContentHandler extends DefaultHandler {
private String nodeName;
private StringBuilder id;
private StringBuilder name;
private StringBuilder version;
//在开始XML解析的时候调用
@Override
public void startDocument() throws SAXException {
id = new StringBuilder();
name = new StringBuilder();
version = new StringBuilder();
}
//在开始解析某个节点时候调用
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
//记录当前节点名
nodeName = localName;
}
//在获取节点内容的时候调用
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
//根据当前节点名判断将内容添加到哪一个StringBuilder对象中
if ("id".equals(nodeName)){
id.append(ch,start,length);
}else if ("name".equals(nodeName)){
name.append(ch, start, length);
}else if ("version".equals(nodeName)){
version.append(ch, start, length);
}
}
//在完成解析某个节点的时候调用
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if ("app".equals(localName)){
Log.d("ContentHandler","id is"+id.toString().trim());
Log.d("ContentHandler","name is"+name.toString().trim());
Log.d("ContentHandler","version is"+version.toString().trim());
//最后要将StringBuilder清除掉
id.setLength(0);
name.setLength(0);
version.setLength(0);
}
}
//在完成整个XML解析的时候调用
@Override
public void endDocument() throws SAXException {
super.endDocument();
}
}
然后再得到了服务器数据后就要到MainActivity中创建一个方法把数据解析出来
@Override
public void endDocument() throws SAXException {
super.endDocument();
}
}
private void parseXMLWithSAX(String xmlData){
try{
SAXParserFactory factory = SAXParserFactory.newInstance();
XMLReader xmlReader = factory.newSAXParser().getXMLReader();
ContentHandler handler = new ContentHandler();
//将ContentHandler实例设置到XMLReader中
xmlReader.setContentHandler(handler);
//开始执行解析
xmlReader.parse(new InputSource(new StringReader(xmlData)));
}catch (Exception e){
e.printStackTrace();
}
}
DOM解析:
/**
* dom解析,该方法里的代码演示了如何利用DOM解析,拿到该xml文档的所有信息,如根节点名称,属性,和该根节点包含的
* 所有子节点的所有信息(子节点的属性,文本内容,子节点又包含的子节点等等)
*/
public void DomParse(View view) {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder builder = factory.newDocumentBuilder();
//从assets目录获取xml文档输入流
AssetManager assets = getAssets();
InputStream is = assets.open("text.xml");
//通过 builder.parse(is)方法获取一个document实例
Document document = builder.parse(is);
//根节点相关
Element element = document.getDocumentElement();//获取当前xml文档的所有信息
element.getTagName();//返回根节点的名称,这里为:note
element.getAttribute("name");//根据根节点的属性名返回属性值,这里为:rqq
//子节点相关
NodeList nodeList = element.getElementsByTagName("*");//根据子节点的节点名返回一个节点列表,如果填写"*",则返回所有节点的列表信息
nodeList.item(0).getNodeName();//返回第0个位置子节点的节点名,这里为:to
nodeList.item(0).getTextContent();//返回第0个位置子节点里的文本内容,不是属性值,这里为:George
nodeList.item(0).getParentNode();//返回第0个位置子节点的父节点,因各种原因找不到父节点则返回null,这里为:note
nodeList.item(0).getChildNodes();//返回第0个位置子节点包含的所有子节点列表信息,没有子节点,也会返回一个NodeList,只不过长度为0,这里当前的子节点<span style="white-space:pre"> </span> //下面已经没有子节点了,所以返回的NodeList长度为0
NamedNodeMap map = nodeList.item(0).getAttributes();//获取第0个位置子节点所有的属性集合,这里只有1个属性,即id
String nodeValue = map.item(0).getNodeValue();//获取第0个位置子节点的第0个位置属性的属性值,这里为:1
/*
* //也可通过下面的方式获取
* //因为Element是Node的子类,所以可以把Node强转为Element
* Element element1 = (Element) nodeList.item(0);
* //然后通过Element拿到该子节点的各种属性值
* element1.getAttribute("id");//获取该子节点的id属性
* //因为Element继承自Node,所以Element可以使用父类即Node类的所有的public方法,如下:
* element1.getNodeName();
* element1.getTextContent();
* element1.getParentNode();
* element1.getChildNodes();
*
* */
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
}
}
好了以上三种解析方式都给大家列出来了使用方法。也算是各有所长吧。在解析的时候可以选择自己适合的解析方式来使用。