本讲内容:使用 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
上面网址查询的结果如下所示:
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 > |
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 > |
14 | < CONDITION data = "晴" /> |
17 | < HUMIDITY data = "湿度: 61%" /> |
18 | < ICON data = "/ig/images/weather/sunny.gif" /> |
19 | < WIND_CONDITION data = "风向: 北、风速:0 米/秒" /> |
22 | < DAY_OF_WEEK data = "周二" /> |
25 | < ICON data = "/ig/images/weather/sunny.gif" /> |
26 | < CONDITION data = "晴" /> |
27 | </ FORECAST_CONDITIONS > |
29 | < DAY_OF_WEEK data = "周三" /> |
32 | < ICON data = "/ig/images/weather/cn_cloudy.gif" /> |
33 | < CONDITION data = "多云" /> |
34 | </ FORECAST_CONDITIONS > |
36 | < DAY_OF_WEEK data = "周四" /> |
39 | < ICON data = "/ig/images/weather/sunny.gif" /> |
40 | < CONDITION data = "晴" /> |
41 | </ FORECAST_CONDITIONS > |
其次我们可以根据城市名称的汉语拼音来获取天气信息。
http://www.google.com/ig/api?hl=zh-cn&ie=utf-8&weather=zhengzhou
上面网址的查询结果如下所示:
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> |
14 | <CONDITION data= "雾霾" /> |
17 | <HUMIDITY data= "湿度: 43%" /> |
18 | <ICON data= "/ig/images/weather/haze.gif" /> |
19 | <WIND_CONDITION data= "风向: 北、风速:2 米/秒" /> |
22 | <DAY_OF_WEEK data= "周二" /> |
25 | <ICON data= "/ig/images/weather/mostly_sunny.gif" /> |
26 | <CONDITION data= "晴间多云" /> |
27 | </FORECAST_CONDITIONS> |
29 | <DAY_OF_WEEK data= "周三" /> |
32 | <ICON data= "/ig/images/weather/sunny.gif" /> |
33 | <CONDITION data= "晴" /> |
34 | </FORECAST_CONDITIONS> |
36 | <DAY_OF_WEEK data= "周四" /> |
39 | <ICON data= "/ig/images/weather/sunny.gif" /> |
40 | <CONDITION data= "晴" /> |
41 | </FORECAST_CONDITIONS> |
43 | <DAY_OF_WEEK data= "周五" /> |
46 | <ICON data= "/ig/images/weather/mostly_sunny.gif" /> |
47 | <CONDITION data= "以晴为主" /> |
48 | </FORECAST_CONDITIONS> |
顺便说一下,我们通过 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; |
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; |
09 | public class MainActivity extends Activity { |
12 | public void onCreate(Bundle savedInstanceState) { |
13 | super .onCreate(savedInstanceState); |
14 | setContentView(R.layout.main); |
17 | Button b1 = (Button) findViewById(R.id.button1); |
18 | final TextView tv1 = (TextView) findViewById(R.id.textView1); |
21 | b1.setOnClickListener( new View.OnClickListener() { |
23 | public void onClick(View arg0) { |
27 | String googleWeatherString = HttpClientConnector.getStringByUrl(googleWeatherUrl); |
29 | TomorrowWeatherVO tomorrowWeatherVO = TomorrowWeatherParse.parse(googleWeatherString); |
31 | if (tomorrowWeatherVO!= null ){ |
32 | tv1.setText( "明日天气情况:" + tomorrowWeatherVO.getCondition() + " 最高气温:" + tomorrowWeatherVO.getHigh() |
33 | + " 最低气温:" + tomorrowWeatherVO.getLow()); |
3、上面使用的HttpClientConnector工具类代码如下:
01 | package basic.android.xml.sax; |
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; |
08 | import android.util.Log; |
10 | public class HttpClientConnector { |
12 | static String getStringByUrl(String url) { |
14 | String outputString = "" ; |
17 | DefaultHttpClient httpclient = new DefaultHttpClient(); |
19 | HttpGet httpget = new HttpGet(url); |
21 | ResponseHandler<STRING> responseHandler = new BasicResponseHandler(); |
24 | outputString = httpclient.execute(httpget, responseHandler); |
26 | } catch (Exception e) { |
30 | httpclient.getConnectionManager().shutdown(); |
4、SAX解析器 TomorrowWeatherParse.java的代码如下:
01 | package basic.android.xml.sax; |
03 | import java.io.IOException; |
04 | import java.io.StringReader; |
06 | import javax.xml.parsers.ParserConfigurationException; |
07 | import javax.xml.parsers.SAXParserFactory; |
09 | import org.xml.sax.InputSource; |
10 | import org.xml.sax.SAXException; |
11 | import org.xml.sax.XMLReader; |
13 | public class TomorrowWeatherParse { |
16 | public static TomorrowWeatherVO parse(String googleWeatherString) { |
18 | SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); |
20 | TomorrowWeatherVO tomorrowWeatherVO = new TomorrowWeatherVO(); |
23 | XMLReader xmlReader = saxParserFactory.newSAXParser().getXMLReader(); |
24 | WeatherXMLHandler handler = new WeatherXMLHandler(tomorrowWeatherVO); |
25 | xmlReader.setContentHandler(handler); |
27 | xmlReader.parse( new InputSource( new StringReader(googleWeatherString))); |
29 | } catch (SAXException e) { |
31 | } catch (ParserConfigurationException e) { |
33 | } catch (IOException e) { |
37 | return tomorrowWeatherVO; |
5、TomorrowWeatherParse.java 中使用到的内容处理器 WeatherXMLHandler.java的代码如下:
01 | package basic.android.xml.sax; |
03 | import org.xml.sax.Attributes; |
04 | import org.xml.sax.SAXException; |
05 | import org.xml.sax.helpers.DefaultHandler; |
06 | import android.util.Log; |
08 | public class WeatherXMLHandler extends DefaultHandler { |
11 | TomorrowWeatherVO tomorrowWeatherVO; |
17 | public WeatherXMLHandler() { |
22 | public WeatherXMLHandler(TomorrowWeatherVO tomorrowWeatherVO) { |
23 | this .tomorrowWeatherVO = tomorrowWeatherVO; |
84 | public void characters( char [] ch, int start, int length) throws SAXException { |
85 | super .characters(ch, start, length); |
上面的代码里有好多空方法,是为了让你了解默认的内容处理器DefaultHandler中的常用方法,其中因为google天气xml的特殊结构,让我们没有机会使用一个更常用的方法characters,很是遗憾,大家自己找资料学习吧。
6、最后还有一个,存储明日天气信息的Bean:TomorrowWeatherVO.java
01 | package basic.android.xml.sax; |
03 | public class TomorrowWeatherVO { |
10 | public String getLow() { |
13 | public void setLow(String low) { |
16 | public String getHigh() { |
19 | public void setHigh(String high) { |
22 | public String getIcon() { |
25 | public void setIcon(String icon) { |
28 | public String getCondition() { |
31 | public void setCondition(String condition) { |
32 | this .condition = condition; |
35 | public TomorrowWeatherVO(String low, String high, String icon, |
41 | this .condition = condition; |
44 | public TomorrowWeatherVO() { |
7、照例还是把简陋的布局文件贴出来main.xml
1 | <?xml version= "1.0" encoding= "utf-8" ?> |
3 | <BUTTON type=submit android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:text= "获取明天天气情况" android:id= "@+id/button1" > |
5 | <TEXTVIEW android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:text= "" android:id= "@+id/textView1" > |
8、最后不要忘了在AndroidManifest.xml中加入 访问互联网的权限:
1 | <USES android:name= "android.permission.INTERNET" -permission></USES> |
好,我们可以编译并运行程序,查看结果了:
![image image](http://android.yaohuiji.com/wp-content/uploads/2011/03/image_thumb.png)
点击按钮:
![image image](http://android.yaohuiji.com/wp-content/uploads/2011/03/image_thumb1.png)
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; |
03 | import java.io.IOException; |
04 | import java.io.StringReader; |
06 | import org.xmlpull.v1.XmlPullParser; |
07 | import org.xmlpull.v1.XmlPullParserException; |
08 | import org.xmlpull.v1.XmlPullParserFactory; |
10 | import android.util.Log; |
12 | public class TomorrowWeatherPullParse { |
15 | public static TomorrowWeatherVO parse(String googleWeatherString) { |
17 | Log.i( "yao" , "TomorrowWeatherPullParse.parse" ); |
23 | TomorrowWeatherVO tomorrowWeatherVO = new TomorrowWeatherVO(); |
28 | XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); |
31 | XmlPullParser parser = factory.newPullParser(); |
34 | parser.setInput( new StringReader(googleWeatherString)); |
37 | int eventType = parser.getEventType(); |
40 | while (eventType != XmlPullParser.END_DOCUMENT) { |
43 | case XmlPullParser.START_DOCUMENT: |
46 | case XmlPullParser.START_TAG: |
48 | String tagName = parser.getName(); |
50 | if (tagName.equals( "forecast_conditions" )) { |
55 | if (tagName.equals( "low" )) { |
57 | tomorrowWeatherVO.setLow(parser.getAttributeValue( 0 )); |
59 | if (tagName.equals( "high" )) { |
60 | tomorrowWeatherVO.setHigh(parser.getAttributeValue( 0 )); |
62 | if (tagName.equals( "icon" )) { |
63 | tomorrowWeatherVO.setIcon(parser.getAttributeValue( 0 )); |
65 | if (tagName.equals( "condition" )) { |
66 | Log.i( "yao" , "condition=" + parser.getAttributeValue( 0 )); |
67 | tomorrowWeatherVO.setCondition(parser.getAttributeValue( 0 )); |
71 | case XmlPullParser.END_TAG: |
73 | case XmlPullParser.END_DOCUMENT: |
78 | eventType = parser.next(); |
81 | } catch (XmlPullParserException e) { |
83 | } catch (IOException e) { |
87 | return tomorrowWeatherVO; |
编译和运行结果和上面的项目一模一样,我也就不上图了。我们可以看到pull解析方式更简单直接些,代码也少一些,至少省了一个handler文件,不是吗。好了本讲就到这里,祝愉快。