在Android中解析XML
本讲内容:使用 SAX 和 pull 解析器解析XML
(说明:本讲写的比较晚所以采用了Android2.3.3版本,其他的也一样,我会尽量在课件里使用最新版本的API。)
在Android中解析XML常用的有三种方法:SAX、DOM 和 pull ,三种方法各有优劣。本讲将用一个google天气预报的实例来和大家一起学习如何使用SAX和pull的方式XML解析。
一、Google天气预报API介绍
我们上一讲的时候使用过Google Weather API,这里要说明的是Google Weather API 并不是官方提供的,是非公开的API,你可以拿来用,但是不能保证准确和及时。
首先我们可以根据经纬度来获取天气信息。
http://www.google.com/ig/api?hl=zh-cn&ie=utf-8&weather=,,,34720001,113650001
上面网址查询的结果如下所示:
01 |
<?xml version="1.0"?> |
02 |
<XML_API_REPLY version="1"> |
03 |
<WEATHER module_id="0" tab_id="0" mobile_row="0" mobile_zipped="1" row="0" section="0"> |
04 |
<FORECAST_INFORMATION> |
05 |
<CITY data="" /> |
06 |
<POSTAL_CODE data="" /> |
07 |
<LATITUDE_E6 data="34720001" /> |
08 |
<LONGITUDE_E6 data="113650001" /> |
09 |
<FORECAST_DATE data="2011-03-08" /> |
10 |
<CURRENT_DATE_TIME data="2011-03-08 14:00:00 +0000" /> |
11 |
<UNIT_SYSTEM data="SI" /> |
12 |
</FORECAST_INFORMATION> |
13 |
<CURRENT_CONDITIONS> |
14 |
<CONDITION data="晴" /> |
15 |
<TEMP_F data="" /> |
16 |
<TEMP_C data="" /> |
17 |
<HUMIDITY data="湿度: 61%" /> |
18 |
<ICON data="/ig/images/weather/sunny.gif" /> |
19 |
<WIND_CONDITION data="风向: 北、风速:0 米/秒" /> |
20 |
</CURRENT_CONDITIONS> |
21 |
<FORECAST_CONDITIONS> |
22 |
<DAY_OF_WEEK data="周二" /> |
23 |
<LOW data="3" /> |
24 |
<HIGH data="16" /> |
25 |
<ICON data="/ig/images/weather/sunny.gif" /> |
26 |
<CONDITION data="晴" /> |
27 |
</FORECAST_CONDITIONS> |
28 |
<FORECAST_CONDITIONS> |
29 |
<DAY_OF_WEEK data="周三" /> |
30 |
<LOW data="2" /> |
31 |
<HIGH data="12" /> |
32 |
<ICON data="/ig/images/weather/cn_cloudy.gif" /> |
33 |
<CONDITION data="多云" /> |
34 |
</FORECAST_CONDITIONS> |
35 |
<FORECAST_CONDITIONS> |
36 |
<DAY_OF_WEEK data="周四" /> |
37 |
<LOW data="2" /> |
38 |
<HIGH data="15" /> |
39 |
<ICON data="/ig/images/weather/sunny.gif" /> |
40 |
<CONDITION data="晴" /> |
41 |
</FORECAST_CONDITIONS> |
42 |
</WEATHER> |
43 |
</XML_API_REPLY> |
其次我们可以根据城市名称的汉语拼音来获取天气信息。
http://www.google.com/ig/api?hl=zh-cn&ie=utf-8&weather=zhengzhou
上面网址的查询结果如下所示:
01 |
<?xml version="1.0"?> |
02 |
<XML_API_REPLY version="1"> |
03 |
<WEATHER module_id="0" tab_id="0" mobile_row="0" mobile_zipped="1" row="0" section="0"> |
04 |
<FORECAST_INFORMATION> |
05 |
<CITY data="Zhengzhou, Henan" /> |
06 |
<POSTAL_CODE data="zhengzhou" /> |
07 |
<LATITUDE_E6 data="" /> |
08 |
<LONGITUDE_E6 data="" /> |
09 |
<FORECAST_DATE data="2011-03-08" /> |
10 |
<CURRENT_DATE_TIME data="2011-03-08 16:00:00 +0000" /> |
11 |
<UNIT_SYSTEM data="SI" /> |
12 |
</FORECAST_INFORMATION> |
13 |
<CURRENT_CONDITIONS> |
14 |
<CONDITION data="雾霾" /> |
15 |
<TEMP_F data="50" /> |
16 |
<TEMP_C data="10" /> |
17 |
<HUMIDITY data="湿度: 43%" /> |
18 |
<ICON data="/ig/images/weather/haze.gif" /> |
19 |
<WIND_CONDITION data="风向: 北、风速:2 米/秒" /> |
20 |
</CURRENT_CONDITIONS> |
21 |
<FORECAST_CONDITIONS> |
22 |
<DAY_OF_WEEK data="周二" /> |
23 |
<LOW data="4" /> |
24 |
<HIGH data="14" /> |
25 |
<ICON data="/ig/images/weather/mostly_sunny.gif" /> |
26 |
<CONDITION data="晴间多云" /> |
27 |
</FORECAST_CONDITIONS> |
28 |
<FORECAST_CONDITIONS> |
29 |
<DAY_OF_WEEK data="周三" /> |
30 |
<LOW data="1" /> |
31 |
<HIGH data="11" /> |
32 |
<ICON data="/ig/images/weather/sunny.gif" /> |
33 |
<CONDITION data="晴" /> |
34 |
</FORECAST_CONDITIONS> |
35 |
<FORECAST_CONDITIONS> |
36 |
<DAY_OF_WEEK data="周四" /> |
37 |
<LOW data="3" /> |
38 |
<HIGH data="15" /> |
39 |
<ICON data="/ig/images/weather/sunny.gif" /> |
40 |
<CONDITION data="晴" /> |
41 |
</FORECAST_CONDITIONS> |
42 |
<FORECAST_CONDITIONS> |
43 |
<DAY_OF_WEEK data="周五" /> |
44 |
<LOW data="7" /> |
45 |
<HIGH data="19" /> |
46 |
<ICON data="/ig/images/weather/mostly_sunny.gif" /> |
47 |
<CONDITION data="以晴为主" /> |
48 |
</FORECAST_CONDITIONS> |
49 |
</WEATHER> |
50 |
</XML_API_REPLY> |
顺便说一下,我们通过 http://www.google.com/ig/cities?output=xml&hl=zh-cn&country=cn 查到郑州的经纬度是(经度113650001,纬度34720001),那么也就是说通过查询经度113650001,纬度34720001处的天气和查找郑州的天气应该是一致的了,实际上你也看到了,上面两次查询的结果并不相同。好在我们出于学习目的这点小误差不是我们考虑的问题。
简单分析一下上述XML文件,会发现第二个forecast_conditions标签里面就是我们需要的明日天气预报信息,包括有最高、最低气温、天气情况描述和天气描述图片。
二、使用SAX解析Google Weather
DOM解析在Android开发里一般是不被推荐的,因为DOM需要把整个XML文件都读到内存里,才能组装成一个树形结构,虽然这样的树形结构我们用起来很舒服,可是它的内存开销在很多时候是难以承受的。
而SAX(Simple API for XML)则提供了一种基于事件的处理思路,他不需要装载、遍历整个XML文件,只要发现你所关心的标签或者数据,就可以随时停止解析。这在资源比较紧缺的智能手机领域里,还是显得非常有价值的。废话不说,我们还是用一个例子来展示如何使用SAX来解析XML文件,我会同样把讲解写在文档的注释里。如果同学们看着还是辛苦的话,建议找些SAX的相关知识先期补习一下。
1、新建一个项目 Lesson31_XmlSaxParser
2、在MainActivit.java的代码如下:
01 |
package basic.android.xml.sax; |
02 |
|
03 |
import android.app.Activity; |
04 |
import android.os.Bundle; |
05 |
import android.view.View; |
06 |
import android.widget.Button; |
07 |
import android.widget.TextView; |
08 |
|
09 |
public class MainActivity extends Activity { |
10 |
|
11 |
@Override |
12 |
public void onCreate(Bundle savedInstanceState) { |
13 |
super.onCreate(savedInstanceState); |
14 |
setContentView(R.layout.main); |
15 |
|
16 |
//定义UI组件 |
17 |
Button b1 = (Button) findViewById(R.id.button1); |
18 |
final TextView tv1 = (TextView) findViewById(R.id.textView1); |
19 |
|
20 |
//为按钮绑定监听器 |
21 |
b1.setOnClickListener(new View.OnClickListener() { |
22 |
@Override |
23 |
public void onClick(View arg0) { |
24 |
//定义一个查询Google天气的字符串,后面的经纬度我写死成郑州的坐标了,你懂的 |
25 |
String googleWeatherUrl = "http://www.google.com/ig/api?hl=zh-cn&ie=utf-8&weather=,,,34720001,113650001"; |
26 |
//定义了一个HttpClientConnector工具类用来把google天气预报返回的XML信息存储在一个字符串里,这里可能会有聪明的同学说,你已经把整个xml都读回来了,还扯什么读一半就可以退出的话,这里要说明的是google Weather API很蛋疼,直接用sax解析会出错,所以只能先完整读回来 |
27 |
String googleWeatherString = HttpClientConnector.getStringByUrl(googleWeatherUrl); |
28 |
//定义一个SAX Parse对象把xml的字符串解析成我们要的 明日天气信息Bean |
29 |
TomorrowWeatherVO tomorrowWeatherVO = TomorrowWeatherParse.parse(googleWeatherString); |
30 |
//显示天气信息 |
31 |
if(tomorrowWeatherVO!=null){ |
32 |
tv1.setText("明日天气情况:" + tomorrowWeatherVO.getCondition() + " 最高气温:" + tomorrowWeatherVO.getHigh() |
33 |
+ " 最低气温:" + tomorrowWeatherVO.getLow()); |
34 |
} |
35 |
} |
36 |
}); |
37 |
} |
38 |
|
39 |
} |
3、上面使用的HttpClientConnector工具类代码如下:
01 |
package basic.android.xml.sax; |
02 |
|
03 |
import org.apache.http.client.ResponseHandler; |
04 |
import org.apache.http.client.methods.HttpGet; |
05 |
import org.apache.http.impl.client.BasicResponseHandler; |
06 |
import org.apache.http.impl.client.DefaultHttpClient; |
07 |
|
08 |
import android.util.Log; |
09 |
|
10 |
public class HttpClientConnector { |
11 |
|
12 |
static String getStringByUrl(String url) { |
13 |
|
14 |
String outputString = ""; |
15 |
|
16 |
// DefaultHttpClient |
17 |
DefaultHttpClient httpclient = new DefaultHttpClient(); |
18 |
// HttpGet |
19 |
HttpGet httpget = new HttpGet(url); |
20 |
// ResponseHandler |
21 |
ResponseHandler<STRING> responseHandler = new BasicResponseHandler(); |
22 |
|
23 |
try { |
24 |
outputString = httpclient.execute(httpget, responseHandler); |
25 |
Log.i("yao", "连接成功"); |
26 |
} catch (Exception e) { |
27 |
Log.i("yao", "连接失败"); |
28 |
e.printStackTrace(); |
29 |
} |
30 |
httpclient.getConnectionManager().shutdown(); |
31 |
return outputString; |
32 |
|
33 |
} |
34 |
|
35 |
}</STRING> |
4、SAX解析器 TomorrowWeatherParse.java的代码如下:
01 |
package basic.android.xml.sax; |
02 |
|
03 |
import java.io.IOException; |
04 |
import java.io.StringReader; |
05 |
|
06 |
import javax.xml.parsers.ParserConfigurationException; |
07 |
import javax.xml.parsers.SAXParserFactory; |
08 |
|
09 |
import org.xml.sax.InputSource; |
10 |
import org.xml.sax.SAXException; |
11 |
import org.xml.sax.XMLReader; |
12 |
|
13 |
public class TomorrowWeatherParse { |
14 |
|
15 |
// 解析天气预报字符串成一个天气信息对象 |
16 |
public static TomorrowWeatherVO parse(String googleWeatherString) { |
17 |
|
18 |
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); |
19 |
|
20 |
TomorrowWeatherVO tomorrowWeatherVO = new TomorrowWeatherVO(); |
21 |
|
22 |
try { |
23 |
XMLReader xmlReader = saxParserFactory.newSAXParser().getXMLReader(); |
24 |
WeatherXMLHandler handler = new WeatherXMLHandler(tomorrowWeatherVO); |
25 |
xmlReader.setContentHandler(handler); |
26 |
|
27 |
xmlReader.parse(new InputSource(new StringReader(googleWeatherString))); |
28 |
|
29 |
} catch (SAXException e) { |
30 |
e.printStackTrace(); |
31 |
} catch (ParserConfigurationException e) { |
32 |
e.printStackTrace(); |
33 |
} catch (IOException e) { |
34 |
e.printStackTrace(); |
35 |
} |
36 |
|
37 |
return tomorrowWeatherVO; |
38 |
|
39 |
} |
40 |
|
41 |
} |
5、TomorrowWeatherParse.java 中使用到的内容处理器 WeatherXMLHandler.java的代码如下:
01 |
package basic.android.xml.sax; |
02 |
|
03 |
import org.xml.sax.Attributes; |
04 |
import org.xml.sax.SAXException; |
05 |
import org.xml.sax.helpers.DefaultHandler; |
06 |
import android.util.Log; |
07 |
|
08 |
public class WeatherXMLHandler extends DefaultHandler { |
09 |
|
10 |
// 明日天气预报Bean |
11 |
TomorrowWeatherVO tomorrowWeatherVO; |
12 |
|
13 |
// 记录出现次数 |
14 |
int findCount = 0; |
15 |
|
16 |
// 默认构造方法 |
17 |
public WeatherXMLHandler() { |
18 |
super(); |
19 |
} |
20 |
|
21 |
// 构造方法 |
22 |
public WeatherXMLHandler(TomorrowWeatherVO tomorrowWeatherVO) { |
23 |
this.tomorrowWeatherVO = tomorrowWeatherVO; |
24 |
} |
25 |
|
26 |
/* |
27 |
* 文档结束时触发 |
28 |
*/ |
29 |
@Override |
30 |
public void endDocument() throws SAXException { |
31 |
Log.i("yao", "文档解析结束"); |
32 |
super.endDocument(); |
33 |
} |
34 |
|
35 |
/* |
36 |
* 文档开始时触发 |
37 |
*/ |
38 |
@Override |
39 |
public void startDocument() throws SAXException { |
40 |
Log.i("yao", "文档解析开始"); |
41 |
super.startDocument(); |
42 |
} |
43 |
|
44 |
/* |
45 |
* 元素开始时触发 |
46 |
*/ |
47 |
@Override |
48 |
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { |
49 |
Log.i("yao", qName); |
50 |
if (qName.equals("forecast_conditions")) { |
51 |
findCount++; |
52 |
} |
53 |
Log.i("yao", "" + findCount); |
54 |
if (findCount == 2) { |
55 |
if (qName.equals("low")) { |
56 |
tomorrowWeatherVO.setLow(attributes.getValue("data")); |
57 |
} |
58 |
if (qName.equals("high")) { |
59 |
tomorrowWeatherVO.setHigh(attributes.getValue("data")); |
60 |
} |
61 |
if (qName.equals("icon")) { |
62 |
tomorrowWeatherVO.setIcon(attributes.getValue("data")); |
63 |
} |
64 |
if (qName.equals("condition")) { |
65 |
tomorrowWeatherVO.setCondition(attributes.getValue("data")); |
66 |
} |
67 |
} |
68 |
super.startElement(uri, localName, qName, attributes); |
69 |
} |
70 |
|
71 |
/* |
72 |
* 元素结束时触发 |
73 |
*/ |
74 |
@Override |
75 |
public void endElement(String uri, String localName, String qName) throws SAXException { |
76 |
Log.i("yao", "元素解析结束"); |
77 |
super.endElement(uri, localName, qName); |
78 |
} |
79 |
|
80 |
/* |
81 |
* 读取元素内容 |
82 |
*/ |
83 |
@Override |
84 |
public void characters(char[] ch, int start, int length) throws SAXException { |
85 |
super.characters(ch, start, length); |
86 |
} |
87 |
|
88 |
} |
上面的代码里有好多空方法,是为了让你了解默认的内容处理器DefaultHandler中的常用方法,其中因为google天气xml的特殊结构,让我们没有机会使用一个更常用的方法characters,很是遗憾,大家自己找资料学习吧。
6、最后还有一个,存储明日天气信息的Bean:TomorrowWeatherVO.java
01 |
package basic.android.xml.sax; |
02 |
|
03 |
public class TomorrowWeatherVO { |
04 |
|
05 |
String low; |
06 |
String high; |
07 |
String icon; |
08 |
String condition; |
09 |
|
10 |
public String getLow() { |
11 |
return low; |
12 |
} |
13 |
public void setLow(String low) { |
14 |
this.low = low; |
15 |
} |
16 |
public String getHigh() { |
17 |
return high; |
18 |
} |
19 |
public void setHigh(String high) { |
20 |
this.high = high; |
21 |
} |
22 |
public String getIcon() { |
23 |
return icon; |
24 |
} |
25 |
public void setIcon(String icon) { |
26 |
this.icon = icon; |
27 |
} |
28 |
public String getCondition() { |
29 |
return condition; |
30 |
} |
31 |
public void setCondition(String condition) { |
32 |
this.condition = condition; |
33 |
} |
34 |
|
35 |
public TomorrowWeatherVO(String low, String high, String icon, |
36 |
String condition) { |
37 |
super(); |
38 |
this.low = low; |
39 |
this.high = high; |
40 |
this.icon = icon; |
41 |
this.condition = condition; |
42 |
} |
43 |
|
44 |
public TomorrowWeatherVO() { |
45 |
|
46 |
} |
47 |
} |
7、照例还是把简陋的布局文件贴出来main.xml
1 |
<?xml version="1.0" encoding="utf-8"?> |
2 |
<LINEARLAYOUT xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> |
3 |
<BUTTON type=submit android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="获取明天天气情况" android:id="@+id/button1"> |
4 |
</BUTTON> |
5 |
<TEXTVIEW android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="" android:id="@+id/textView1"> |
6 |
</TEXTVIEW> |
7 |
</LINEARLAYOUT> |
8、最后不要忘了在AndroidManifest.xml中加入 访问互联网的权限:
1 |
<USES android:name="android.permission.INTERNET" -permission></USES> |
好,我们可以编译并运行程序,查看结果了:
点击按钮:
OK,我们发现和QQ的天气预报信息还是满切合的,是不是有那么一点点成就感?
三、使用pull解析Google Weather
pull解析XML的方式和SAX比较相近,它的官网是 http://www.xmlpull.org/ ,Android中集成了pull解析方式,因此你不必自己找支持库文件。废话不说我们直接上实例。
1、新建一个项目 Lesson31_XmlPullParser
2、重用上面项目的大部分内容,只在解析上替换一下,因此我就把解析器代码贴出来就可以了,TomorrowWeatherPullParse.java的代码如下:
01 |
package basic.android.lesson31; |
02 |
|
03 |
import java.io.IOException; |
04 |
import java.io.StringReader; |
05 |
|
06 |
import org.xmlpull.v1.XmlPullParser; |
07 |
import org.xmlpull.v1.XmlPullParserException; |
08 |
import org.xmlpull.v1.XmlPullParserFactory; |
09 |
|
10 |
import android.util.Log; |
11 |
|
12 |
public class TomorrowWeatherPullParse { |
13 |
|
14 |
// 解析天气预报字符串成一个天气信息对象 |
15 |
public static TomorrowWeatherVO parse(String googleWeatherString) { |
16 |
|
17 |
Log.i("yao", "TomorrowWeatherPullParse.parse"); |
18 |
|
19 |
// 记录出现次数 |
20 |
int findCount = 0; |
21 |
|
22 |
// 明日天气预报Bean |
23 |
TomorrowWeatherVO tomorrowWeatherVO = new TomorrowWeatherVO(); |
24 |
|
25 |
try { |
26 |
|
27 |
//定义工厂 XmlPullParserFactory |
28 |
XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); |
29 |
|
30 |
//定义解析器 XmlPullParser |
31 |
XmlPullParser parser = factory.newPullParser(); |
32 |
|
33 |
//获取xml输入数据 |
34 |
parser.setInput(new StringReader(googleWeatherString)); |
35 |
|
36 |
//开始解析事件 |
37 |
int eventType = parser.getEventType(); |
38 |
|
39 |
//处理事件,不碰到文档结束就一直处理 |
40 |
while (eventType != XmlPullParser.END_DOCUMENT) { |
41 |
//因为定义了一堆静态常量,所以这里可以用switch |
42 |
switch (eventType) { |
43 |
case XmlPullParser.START_DOCUMENT: |
44 |
break; |
45 |
|
46 |
case XmlPullParser.START_TAG: |
47 |
//给当前标签起个名字 |
48 |
String tagName = parser.getName(); |
49 |
//看到感兴趣的标签个计数 |
50 |
if (tagName.equals("forecast_conditions")) { |
51 |
findCount++; |
52 |
} |
53 |
//看到要处理的标签,就处理 |
54 |
if (findCount == 2) { |
55 |
if (tagName.equals("low")) { |
56 |
//XML中的属性可以用下面的方法获取,其中0是序号,代表第一个属性 |
57 |
tomorrowWeatherVO.setLow(parser.getAttributeValue(0)); |
58 |
} |
59 |
if (tagName.equals("high")) { |
60 |
tomorrowWeatherVO.setHigh(parser.getAttributeValue(0)); |
61 |
} |
62 |
if (tagName.equals("icon")) { |
63 |
tomorrowWeatherVO.setIcon(parser.getAttributeValue(0)); |
64 |
} |
65 |
if (tagName.equals("condition")) { |
66 |
Log.i("yao", "condition=" + parser.getAttributeValue(0)); |
67 |
tomorrowWeatherVO.setCondition(parser.getAttributeValue(0)); |
68 |
} |
69 |
} |
70 |
break; |
71 |
case XmlPullParser.END_TAG: |
72 |
break; |
73 |
case XmlPullParser.END_DOCUMENT: |
74 |
break; |
75 |
} |
76 |
|
77 |
//别忘了用next方法处理下一个事件,忘了的结果就成死循环#_# |
78 |
eventType = parser.next(); |
79 |
} |
80 |
|
81 |
} catch (XmlPullParserException e) { |
82 |
e.printStackTrace(); |
83 |
} catch (IOException e) { |
84 |
e.printStackTrace(); |
85 |
} |
86 |
|
87 |
return tomorrowWeatherVO; |
88 |
} |
89 |
} |
编译和运行结果和上面的项目一模一样,我也就不上图了。我们可以看到pull解析方式更简单直接些,代码也少一些,至少省了一个handler文件,不是吗。好了本讲就到这里,祝愉快。
浙公网安备 33010602011771号