使用Pull和SAX解析XML格式数据
1. Pull解析方式
为了解析方便,这里使用了一个天气服务的api查询返回的xml文档,网址是这个:
http://ws.webxml.com.cn/WebServices/WeatherWS.asmx/getWeather?theCityCode=广州&theUserID=
有点略微尴尬的是这个网站被Chrome识别为有害网站,但本人多次打开表示应该没什么危害。。。
返回的xml大概是这样的:
private void parseXMLWithPull(String xmlData) {
try {
// 首先是创建一个XmlPullParseFactory的实例
XmlPullParserFactory xmlPullParserFactory = XmlPullParserFactory.newInstance();
// 借助这个实例得到XmlPullParser对象
XmlPullParser xmlPullParser = xmlPullParserFactory.newPullParser();
// 调用XmlPullParser的setInput()方法将服务器返回的XML数据设置进去,然后就可以解析了
xmlPullParser.setInput(new StringReader(xmlData));
// 通过getEventType()得到当前的解析事件,然后在一个while循环中不断的进行解析
// 如果当前的解析事件不等于XmlPullParser。END_DOCUMENT, 说明解析工作还没有完成
// 调用next()方法获取下一个解析事件
int eventType = xmlPullParser.getEventType();
List<String> list = new ArrayList<>();
// 在while循环中,通过getName()来获取当前节点的名字,如果发现节点等于string
// 就调用nextText()来获取节点的具体内容
while (eventType != XmlPullParser.END_DOCUMENT) {
switch (eventType) {
// 开始解析某个节点
case XmlPullParser.START_TAG:
String name = xmlPullParser.getName();
if ("string".equals(name)) {
String str = xmlPullParser.nextText();
list.add(str);
}
break;
default:
break;
}
eventType = xmlPullParser.next();
}
showResponse(list);
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
解析出来的文档是这样的:
2. SAX解析方式
一般来说是需要新建一个继承自DefaultHandler的类,并重写父类的5个方法:
public class ConTentHandler extends DefaultHandler {
private String nodeName;
private StringBuilder name;
private List<String> list;
// 在开始XML解析时调用
@Override
public void startDocument() {
name = new StringBuilder();
list = new ArrayList<>();
}
// 在开始解析某个节点时调用
@Override
public void startElement(String uri, String localName, String qNmae, Attributes attributes) {
// 记录当前节点名
nodeName = localName;
}
// 在获取节点中内容时调用
@Override
public void characters(char[] ch, int start, int length) {
// 根据当前节点名判断将内容添加到那个StringBuilder中,这里只有一个StringBuilder
if ("string".equals(nodeName)) {
name.append(ch, start, length);
list.add(name.toString());
}
}
// 在完成解析某个节点的时候调用
@Override
public void endElement(String uri, String localName, String qNmae) {
// 将StringBuilder清空
if ("string".equals(localName)) {
name.setLength(0);
//Log.v("string", name.toString().trim());
}
}
// 在完成整个XML解析时调用
@Override
public void endDocument() throws SAXException {
super.endDocument();
for (int i = 0; i < list.size(); i++){
System.out.println(list.get(i));
}
}
}
在这5个方法中,startElement(), characters(), endElement()是有参数的,从XML中解析出来的数据就会以参数的形式传入到这些方法中,此外在获取节点中的内容时,characters()会被多次调用,一些换行符也被当做内容解析出来,可以调用trim()方法来解决,trim()方法可以去掉字符串首尾的空格。
下面是来自
http://www.cnblogs.com/xiaoluo501395377/p/3444744.html的对于这5个方法的介绍:
startDocument()
当遇到文档的开头的时候,调用这个方法,可以在其中做一些预处理的工作。
endDocument()
和上面的方法相对应,当文档结束的时候,调用这个方法,可以在其中做一些善后的工作。
startElement(String namespaceURI, String localName, String qName, Attributes atts)
当读到一个开始标签的时候,会触发这个方法。namespaceURI就是命名空间,localName是不带命名空间前缀的标签名,qName是带命名空间前缀的标签名。通过atts可以得到所有的属性名和相应的值。要注意的是SAX中一个重要的特点就是它的流式处理,当遇到一个标签的时候,它并不会纪录下以前所碰到的标签,也就是说,在startElement()方法中,所有你所知道的信息,就是标签的名字和属性,至于标签的嵌套结构,上层标签的名字,是否有子元属等等其它与结构相关的信息,都是不得而知的,都需要你的程序来完成。这使得SAX在编程处理上没有DOM来得那么方便。
endElement(String uri, String localName, String name)
这个方法和上面的方法相对应,在遇到结束标签的时候,调用这个方法。
characters(char[] ch, int start, int length)
这个方法用来处理在XML文件中读到的内容,第一个参数用于存放文件的内容,后面两个参数是读到的字符串在这个数组中的起始位置和长度,使用new String(ch,start,length)就可以获取内容。
接下来只需要在MainActivity中增加一个parseXMLWithSAX方法就好了:
private void parseXMLWithSAX(String xmlData) {
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
XMLReader reader = factory.newSAXParser().getXMLReader();
ContentHandler contentHandler = new ConTentHandler();
// 将编写好的ContentHandler的实例设置到XMLReader中
reader.setContentHandler(contentHandler);
// 开始执行解析
reader.parse(new InputSource(new StringReader(xmlData)));
} catch (Exception e) {
e.printStackTrace();
}
}
Pull和SAX解析XML的区别
首先我们知道SAX是事件驱动的,他不会将整个文档读入内存中再去解析,而是一边读取一边解析,所以说文档读取的过程也就是SAX的解析过程。但是虽然SAX解析XML不会将整个文档放进内存中,但是它会遍历文档中的所有节点所以如果我们只是需要文档开头或中间的一小部分内容,这个时候使用SAX就不划算了,因为我们不能停止解析。
SAX解析的工作方式是自动将时间推入到注册的时间处理器中进行处理,所以我们不能控制事件的处理的主动结束。
那么Pull呢?如果说SAX是一种“推”的解析方式,那么Pull就可以说是一种“拉”的解析方式,也就是说应用程序可以根据自己的需求来控制解析器的读取。Pull允许应用程序代码主动从解析器中获取事件,所以可以实现在解析出我们想要的数据后就终止解析。
总之呢,SAX和Pull都有解析速度快,占用内存少的优点,在工作方式上Pull是使用一个循环体,可以随时跳出来,而SAX是只要开始解析了那么久必须全部解析完成(SAX的强迫症无人能敌)。