平常我们在需要数据解析时遇到最多的就是解析JSON数据,但是对于另一种数据解析 —— XML数据解析,却不怎么常见。虽然不常见但是有时候也会有需求遇到它,所以在这里记录下在Android中解析它的两种方式。
Android对于XML解析提供了两种API—————-SAX和PULL。
SAX和PULL的区别:SAX解析器的工作方式是自动将事件推入注册的事件处理器进行处理,因此你不能控制事件的处理主动结束;而Pull解析器的工作方式为允许你的应用程序代码主动从解析器中获取事件,正因为是主动获取事件,因此可以在满足了需要的条件后不再获取事件,结束解析。
我们看一下这两种方式的简单实现。
在解析XML数据时,首先我需要有XML数据,这里我在本地搭建了一个apache服务器,然后写了个XML数据,内容为:
-<apps>
-<app>
<id>1</id>
<name>Goole Map1</name>
<version>1</version>
</app>
-<app>
<id>2</id>
<name>Goole Map2</name>
<version>2</version>
</app>
-<app>
<id>3</id>
<name>Goole Map3</name>
<version>3</version>
</app>
</apps>
一、通过SAX的方式解析XML。
1、首先自定义DefaultHandler的子类ContentHanlders 。
public class ContentHanlders extends DefaultHandler {
private StringBuilder id;
private StringBuilder name;
private StringBuilder version;
private String nodeName;
@Override
public void startDocument () throws SAXException {
id = new StringBuilder();
name = new StringBuilder();
version = new StringBuilder();
}
@Override
public void endDocument () throws SAXException {
super.endDocument();
}
public void startElement (String uri, String localName, String qName, Attributes attributes) throws SAXException {
nodeName = localName;
}
public void endElement (String uri, String localName, String qName) throws SAXException {
if ("app".equals(localName)){
Log.e("tag", "id is "+id.toString().trim());
Log.e("tag", "name is "+name.toString().trim());
Log.e("tag", "version is "+version.toString().trim());
id.setLength(0);
name.setLength(0);
version.setLength(0);
}
}
public void characters (char ch[], int start, int length) throws SAXException {
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);
}
}
}
我们看到这里复写了DefaultHandler的五个方法,分别是:
public void startDocument () —— 当开始XML文档解析时回调这个方法。
public void endDocument () —— 当结束XML文档解析时回调这个方法。
public void startElement (String uri, String localName, String qName, Attributes attributes) —— 当开始解析节点时回调这个方法。
其中参数的意思:
这里为了讲解这几个参数,我们需要另一个XML数据,数据内容为:
<websites
xmlns:sina="http://www.sina.com"
xmlns:baidu="http://www.baidu.com">
<sina:website sina:blog="one.sina.com">我是新浪</sina:website>
<baidu:website baidu:blog="two.baidu.com">我是百度</baidu:website>
</websites>
xmlns:sina=”http://www.sina.com”
xmlns:baidu=”http://www.baidu.com”
这一部分是在定义命名空间(NameSpace),引入的原因是为了避免混淆。例如上面的这个XML文档,sina和baidu都有blog属性,定义了两个namespace,就像sax官网说的,用namespace是为了实现更多的扩展功能。
sina:website sina:blog=”one.sina.com”>我是新浪
对与这一部分
localName = website —— 原始节点名
qname = sina:website —— 命名空间名称+:+原始节点名
uri = http://www.sina.com —— 命名空间的值。
attributes // 对于这个参数我们用一段代码来解释
//打印element的所有属性attributes
for(int i=0; i<attributes.getLength(); i++) {
System.out.println("");
System.out.println(" attribute qName : "+attributes.getQName(i));
System.out.println(" attribute localName: "+attributes.getLocalName(i));
System.out.println(" attribute value : "+attributes.getValue(i));
System.out.println(" attribute uri : "+attributes.getURI(i));
}
输出结果为:
attribute qName : sina:blog —— 当前节点的属性的qname = 命名空间名+:属性名
attribute localName: blog ——当前节点的属性原始名
attribute value : one.sina.com —— 当前节点当前属性的值
attribute uri : http://www.sina.com —— 命名空间的值。
public void endElement (String uri, String localName, String qName) —— 当结束解析某个节点时回调这个方法。
public void characters (char ch[], int start, int length) —— 某个节点对应的值。
2、简单使用。
private void parseXMLwithSAX(String xmlData) {
// 实例化SAMParser工厂
SAXParserFactory spf = SAXParserFactory.newInstance();
try {
//创建SAXParser实例,并获取XML字符读取者
XMLReader reader = spf.newSAXParser().getXMLReader();
//设置解析XML数据处理者
ContentHanlders hanlder = new ContentHanlders();
reader.setContentHandler(hanlder);
//设置待解析的XML数据。
reader.parse(new InputSource(new StringReader(xmlData)));
} catch (SAXException e) {
e.printStackTrace();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
上面就是关于SAX的简单使用。
二、通过PULL的方式解析XML。
如果你对SAX解析数据的方式已经明白,相信这里,你看一眼就知道P**ULL解析XML数据的方式就是对SAX的一种”描述“。通过”描述“我们可以控制解析全过程。**
对于PULL方法解析XML数据要明白四个常用事件
XmlPullParser.START_DOCUMENT; —— 对应SAX的startDocument()
XmlPullParser.END_DOCUMENT; —— 对应SAX的endDocument()
XmlPullParser.START_TAG; —— 对应SAX的startElement(……)
XmlPullParser.END_TAG; —— 对应SAX的endElement(……)
简单实现
private void parseXmlByPull(String xmlData){
try {
//通过XMLPull工厂实例化XMLPull。
XmlPullParser xmlPullParser = XmlPullParserFactory.newInstance().newPullParser();
//设置待解析的XML数据
xmlPullParser.setInput(new StringReader(xmlData));
//获取当前事件
int eventType = xmlPullParser.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT){
//获得当前事件的当前节点名称 ------ 对应SAX的qname。
String nodeName = xmlPullParser.getName();
switch (eventType){
case XmlPullParser.START_TAG:
if ("id".equals(nodeName)){
Log.e(TAG, "id: "+xmlPullParser.nextText() );
}else if ("name".equals(nodeName)){
Log.e(TAG, "name: "+xmlPullParser.nextText() );
}else if ("version".equals(nodeName)){
Log.e(TAG, "version: "+xmlPullParser.nextText() );
}
break;
case XmlPullParser.END_TAG:
if ("app".equals(nodeName)){
Log.e(TAG, "结束一个标签 " );
}
break;
default:break;
}
//获得下一个事件
eventType = xmlPullParser.next();
}
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
通过上面对SAX和PULL的学习,我们知道解析XML数据基本流程:
(1)、开始XML文档解析
(2)、拿到开始节点
(3)、判断是否有子节点
如果没有子节点 :拿到当前节点对应的值。
如果有子节点:按照(2)、(3)、(5)遍历子节点。
(5)、拿到结束节点。
(6)、结束XML文档解析。
学习心得:观看资料 + 思考问题 + 实践证明 + 概括总结 + 整理记录 = 有思想的技术大牛