第三十一讲:在Android中解析XML

108
回复
1030650
查看
  [复制链接]

0

主题

0

帖子

-16

安币

限制会员

发表于 2011-4-29 15:24:45 | 显示全部楼层 |阅读模式
本帖最后由 andbus 于 2011-4-29 15:29 编辑

  本讲内容:使用 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,你可以拿来用,但是不能保证准确和及时。
  首先我们可以根据经纬度来获取天气信息。


上面网址查询的结果如下所示:


  1. <?xml version="1.0"?>
  2. <xml_api_reply version="1">
  3.         <weather section="0" row="0" mobile_zipped="1" mobile_row="0" tab_id="0" module_id="0">
  4.                 <forecast_information>
  5.                         <city data="">
  6. <postal_code data="">
  7.                         <latitude_e6 data="34720001">
  8.                         <longitude_e6 data="113650001">
  9.                         <forecast_date data="2011-03-08">
  10.                         <current_date_time data="2011-03-08 14:00:00 +0000">
  11.                         <unit_system data="SI">
  12.                 </unit_system></current_date_time></forecast_date></longitude_e6></latitude_e6></postal_code></city></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.                 </wind_condition></icon></humidity></temp_c></temp_f></condition></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.                 </condition></icon></high></low></day_of_week></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.                 </condition></icon></high></low></day_of_week></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.                 </condition></icon></high></low></day_of_week></forecast_conditions>
  42.         </weather>
  43. </xml_api_reply>
复制代码

其次我们可以根据城市名称的汉语拼音来获取天气信息。
http://www.google.com/ig/api?hl=zh-cn&ie=utf-8&weather=zhengzhou

上面网址的查询结果如下所示:

  1. <?xml version="1.0"?>
  2. <xml_api_reply version="1">
  3.         <weather section="0" row="0" mobile_zipped="1" mobile_row="0" tab_id="0" module_id="0">
  4.                 <forecast_information>
  5.                         <city data="Zhengzhou, Henan">
  6. <postal_code data="zhengzhou">
  7.                         <latitude_e6 data="">
  8.                         <longitude_e6 data="">
  9.                         <forecast_date data="2011-03-08">
  10.                         <current_date_time data="2011-03-08 16:00:00 +0000">
  11.                         <unit_system data="SI">
  12.                 </unit_system></current_date_time></forecast_date></longitude_e6></latitude_e6></postal_code></city></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.                 </wind_condition></icon></humidity></temp_c></temp_f></condition></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.                 </condition></icon></high></low></day_of_week></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.                 </condition></icon></high></low></day_of_week></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.                 </condition></icon></high></low></day_of_week></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.                 </condition></icon></high></low></day_of_week></forecast_conditions>
  49.         </weather>
  50. </xml_api_reply>
复制代码

  顺便说一下,我们通过 http://www.google.com/ig/cities?output=xml&amp;hl=zh-cn&country=cn 查到郑州的经纬度是(经度113650001,纬度34720001),那么也就是说通过查询经度113650001,纬度34720001处的天气和查找郑州的天气应该是一致的了,实际上你也看到了,上面两次查询的结果并不相同。好在我们出于学习目的这点小误差不是我们考虑的问题。

  简单分析一下上述XML文件,会发现第二个forecast_conditions标签里面就是我们需要的明日天气预报信息,包括有最高、最低气温、天气情况描述和天气描述图片。

  二、使用SAX解析Google WeatherDOM解析在Android开发里一般是不被推荐的,因为DOM需要把整个XML文件都读到内存里,才能组装成一个树形结构,虽然这样的树形结构我们用起来很舒服,可是它的内存开销在很多时候是难以承受的。

  而SAX(Simple API for XML)则提供了一种基于事件的处理思路,他不需要装载、遍历整个XML文件,只要发现你所关心的标签或者数据,就可以随时停止解析。这在资源比较紧缺的智能手机领域里,还是显得非常有价值的。废话不说,我们还是用一个例子来展示如何使用SAX来解析XML文件,我会同样把讲解写在文档的注释里。如果同学们看着还是辛苦的话,建议找些SAX的相关知识先期补习一下。

  1、新建一个项目 Lesson31_XmlSaxParser2、在MainActivit.java的代码如下:
  1. package basic.android.xml.sax;

  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. import android.view.View;
  5. import android.widget.Button;
  6. import android.widget.TextView;

  7. public class MainActivity extends Activity {

  8.         @Override
  9.         public void onCreate(Bundle savedInstanceState) {
  10.                 super.onCreate(savedInstanceState);
  11.                 setContentView(R.layout.main);

  12.                 //定义UI组件
  13.                 Button b1 = (Button) findViewById(R.id.button1);
  14.                 final TextView tv1 = (TextView) findViewById(R.id.textView1);

  15.                 //为按钮绑定监听器
  16.                 b1.setOnClickListener(new View.OnClickListener() {
  17.                         @Override
  18.                         public void onClick(View arg0) {
  19.                                 //定义一个查询Google天气的字符串,后面的经纬度我写死成郑州的坐标了,你懂的
  20.                                 String googleWeatherUrl = "http://www.google.com/ig/api?hl=zh-cn&ie=utf-8&weather=,,,34720001,113650001";
  21.                                 //定义了一个HttpClientConnector工具类用来把google天气预报返回的XML信息存储在一个字符串里,这里可能会有聪明的同学说,你已经把整个xml都读回来了,还扯什么读一半就可以退出的话,这里要说明的是google Weather API很蛋疼,直接用sax解析会出错,所以只能先完整读回来
  22.                                 String googleWeatherString = HttpClientConnector.getStringByUrl(googleWeatherUrl);
  23.                                 //定义一个SAX Parse对象把xml的字符串解析成我们要的 明日天气信息Bean
  24.                                 TomorrowWeatherVO tomorrowWeatherVO = TomorrowWeatherParse.parse(googleWeatherString);
  25.                                 //显示天气信息
  26.                                 if(tomorrowWeatherVO!=null){
  27.                                         tv1.setText("明日天气情况:" + tomorrowWeatherVO.getCondition() + " 最高气温:" + tomorrowWeatherVO.getHigh()
  28.                                                         + " 最低气温:" + tomorrowWeatherVO.getLow());
  29.                                 }
  30.                         }
  31.                 });
  32.         }

  33. }
复制代码
3、上面使用的HttpClientConnector工具类代码如下:
  1. package basic.android.xml.sax;

  2. import org.apache.http.client.ResponseHandler;
  3. import org.apache.http.client.methods.HttpGet;
  4. import org.apache.http.impl.client.BasicResponseHandler;
  5. import org.apache.http.impl.client.DefaultHttpClient;

  6. import android.util.Log;

  7. public class HttpClientConnector {

  8.         static String getStringByUrl(String url) {

  9.                 String outputString = "";

  10.                 // DefaultHttpClient
  11.                 DefaultHttpClient httpclient = new DefaultHttpClient();
  12.                 // HttpGet
  13.                 HttpGet httpget = new HttpGet(url);
  14.                 // ResponseHandler
  15.                 ResponseHandler<string> responseHandler = new BasicResponseHandler();

  16.                 try {
  17.                         outputString = httpclient.execute(httpget, responseHandler);
  18.                         Log.i("yao", "连接成功");
  19.                 } catch (Exception e) {
  20.                         Log.i("yao", "连接失败");
  21.                         e.printStackTrace();
  22.                 }
  23.                 httpclient.getConnectionManager().shutdown();
  24.                 return outputString;

  25.         }

  26. }</string>
复制代码

4、SAX解析器 TomorrowWeatherParse.java的代码如下:
  1. package basic.android.xml.sax;

  2. import java.io.IOException;
  3. import java.io.StringReader;

  4. import javax.xml.parsers.ParserConfigurationException;
  5. import javax.xml.parsers.SAXParserFactory;

  6. import org.xml.sax.InputSource;
  7. import org.xml.sax.SAXException;
  8. import org.xml.sax.XMLReader;

  9. public class TomorrowWeatherParse {

  10.         // 解析天气预报字符串成一个天气信息对象
  11.         public static TomorrowWeatherVO parse(String googleWeatherString) {

  12.                 SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();

  13.                 TomorrowWeatherVO tomorrowWeatherVO = new TomorrowWeatherVO();

  14.                 try {
  15.                         XMLReader xmlReader = saxParserFactory.newSAXParser().getXMLReader();
  16.                         WeatherXMLHandler handler = new WeatherXMLHandler(tomorrowWeatherVO);
  17.                         xmlReader.setContentHandler(handler);

  18.                         xmlReader.parse(new InputSource(new StringReader(googleWeatherString)));

  19.                 } catch (SAXException e) {
  20.                         e.printStackTrace();
  21.                 } catch (ParserConfigurationException e) {
  22.                         e.printStackTrace();
  23.                 } catch (IOException e) {
  24.                         e.printStackTrace();
  25.                 }

  26.                 return tomorrowWeatherVO;

  27.         }

  28. }
复制代码
5、TomorrowWeatherParse.java 中使用到的内容处理器 WeatherXMLHandler.java的代码如下:
  1. package basic.android.xml.sax;

  2. import org.xml.sax.Attributes;
  3. import org.xml.sax.SAXException;
  4. import org.xml.sax.helpers.DefaultHandler;
  5. import android.util.Log;

  6. public class WeatherXMLHandler extends DefaultHandler {

  7.         // 明日天气预报Bean
  8.         TomorrowWeatherVO tomorrowWeatherVO;

  9.         // 记录出现次数
  10.         int findCount = 0;

  11.         // 默认构造方法
  12.         public WeatherXMLHandler() {
  13.                 super();
  14.         }

  15.         // 构造方法
  16.         public WeatherXMLHandler(TomorrowWeatherVO tomorrowWeatherVO) {
  17.                 this.tomorrowWeatherVO = tomorrowWeatherVO;
  18.         }

  19.         /*
  20.          * 文档结束时触发
  21.          */
  22.         @Override
  23.         public void endDocument() throws SAXException {
  24.                 Log.i("yao", "文档解析结束");
  25.                 super.endDocument();
  26.         }

  27.         /*
  28.          * 文档开始时触发
  29.          */
  30.         @Override
  31.         public void startDocument() throws SAXException {
  32.                 Log.i("yao", "文档解析开始");
  33.                 super.startDocument();
  34.         }

  35.         /*
  36.          * 元素开始时触发
  37.          */
  38.         @Override
  39.         public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
  40.                 Log.i("yao", qName);
  41.                 if (qName.equals("forecast_conditions")) {
  42.                         findCount++;
  43.                 }
  44.                 Log.i("yao", "" + findCount);
  45.                 if (findCount == 2) {
  46.                         if (qName.equals("low")) {
  47.                                 tomorrowWeatherVO.setLow(attributes.getValue("data"));
  48.                         }
  49.                         if (qName.equals("high")) {
  50.                                 tomorrowWeatherVO.setHigh(attributes.getValue("data"));
  51.                         }
  52.                         if (qName.equals("icon")) {
  53.                                 tomorrowWeatherVO.setIcon(attributes.getValue("data"));
  54.                         }
  55.                         if (qName.equals("condition")) {
  56.                                 tomorrowWeatherVO.setCondition(attributes.getValue("data"));
  57.                         }
  58.                 }
  59.                 super.startElement(uri, localName, qName, attributes);
  60.         }

  61.         /*
  62.          * 元素结束时触发
  63.          */
  64.         @Override
  65.         public void endElement(String uri, String localName, String qName) throws SAXException {
  66.                 Log.i("yao", "元素解析结束");
  67.                 super.endElement(uri, localName, qName);
  68.         }

  69.         /*
  70.          * 读取元素内容
  71.          */
  72.         @Override
  73.         public void characters(char[] ch, int start, int length) throws SAXException {
  74.                 super.characters(ch, start, length);
  75.         }

  76. }
复制代码
  上面的代码里有好多空方法,是为了让你了解默认的内容处理器DefaultHandler中的常用方法,其中因为google天气xml的特殊结构,让我们没有机会使用一个更常用的方法characters,很是遗憾,大家自己找资料学习吧。

  6、最后还有一个,存储明日天气信息的Bean:TomorrowWeatherVO.java
  1. package basic.android.xml.sax;

  2. public class TomorrowWeatherVO {

  3.         String low;
  4.         String high;
  5.         String icon;
  6.         String condition;

  7.         public String getLow() {
  8.                 return low;
  9.         }
  10.         public void setLow(String low) {
  11.                 this.low = low;
  12.         }
  13.         public String getHigh() {
  14.                 return high;
  15.         }
  16.         public void setHigh(String high) {
  17.                 this.high = high;
  18.         }
  19.         public String getIcon() {
  20.                 return icon;
  21.         }
  22.         public void setIcon(String icon) {
  23.                 this.icon = icon;
  24.         }
  25.         public String getCondition() {
  26.                 return condition;
  27.         }
  28.         public void setCondition(String condition) {
  29.                 this.condition = condition;
  30.         }

  31.         public TomorrowWeatherVO(String low, String high, String icon,
  32.                         String condition) {
  33.                 super();
  34.                 this.low = low;
  35.                 this.high = high;
  36.                 this.icon = icon;
  37.                 this.condition = condition;
  38.         }

  39.         public TomorrowWeatherVO() {

  40.         }
  41. }
复制代码
7、照例还是把简陋的布局文件贴出来main.xml
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <linearlayout android:layout_height="fill_parent" android:layout_width="fill_parent" android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android">
  3.         <button android:layout_height="wrap_content" android:layout_width="wrap_content" android:id="@+id/button1" android:text="获取明天天气情况">
  4.         </button>
  5.         <textview android:layout_height="wrap_content" android:layout_width="wrap_content" android:id="@+id/textView1" android:text="">
  6.         </textview>
  7. </linearlayout>
复制代码
8、最后不要忘了在AndroidManifest.xml中加入 访问互联网的权限:
  1. <uses -permission="" android:name="android.permission.INTERNET"></uses>
复制代码
好,我们可以编译并运行程序,查看结果了:
[/url]
点击按钮:
[url=http://android.yaohuiji.com/wp-content/uploads/2011/03/image1.png]

OK,我们发现和QQ的天气预报信息还是满切合的,是不是有那么一点点成就感?
[/url]
  三、使用pull解析Google Weatherpull解析XML的方式和SAX比较相近,它的官网是 [url]http://www.xmlpull.org/
,Android中集成了pull解析方式,因此你不必自己找支持库文件。废话不说我们直接上实例。

  1、新建一个项目 Lesson31_XmlPullParser2、重用上面项目的大部分内容,只在解析上替换一下,因此我就把解析器代码贴出来就可以了,TomorrowWeatherPullParse.java的代码如下:
  1. package basic.android.lesson31;

  2. import java.io.IOException;
  3. import java.io.StringReader;

  4. import org.xmlpull.v1.XmlPullParser;
  5. import org.xmlpull.v1.XmlPullParserException;
  6. import org.xmlpull.v1.XmlPullParserFactory;

  7. import android.util.Log;

  8. public class TomorrowWeatherPullParse {

  9.         // 解析天气预报字符串成一个天气信息对象
  10.         public static TomorrowWeatherVO parse(String googleWeatherString) {

  11.                 Log.i("yao", "TomorrowWeatherPullParse.parse");

  12.                 // 记录出现次数
  13.                 int findCount = 0;

  14.                 // 明日天气预报Bean
  15.                 TomorrowWeatherVO tomorrowWeatherVO = new TomorrowWeatherVO();

  16.                 try {

  17.                         //定义工厂 XmlPullParserFactory
  18.                         XmlPullParserFactory factory = XmlPullParserFactory.newInstance();

  19.                         //定义解析器 XmlPullParser
  20.                         XmlPullParser parser = factory.newPullParser();

  21.                         //获取xml输入数据
  22.                         parser.setInput(new StringReader(googleWeatherString));

  23.                         //开始解析事件
  24.                         int eventType = parser.getEventType();

  25.                         //处理事件,不碰到文档结束就一直处理
  26.                         while (eventType != XmlPullParser.END_DOCUMENT) {
  27.                                 //因为定义了一堆静态常量,所以这里可以用switch
  28.                                 switch (eventType) {
  29.                                 case XmlPullParser.START_DOCUMENT:
  30.                                         break;

  31.                                 case XmlPullParser.START_TAG:
  32.                                         //给当前标签起个名字
  33.                                         String tagName = parser.getName();
  34.                                         //看到感兴趣的标签个计数
  35.                                         if (tagName.equals("forecast_conditions")) {
  36.                                                 findCount++;
  37.                                         }
  38.                                         //看到要处理的标签,就处理
  39.                                         if (findCount == 2) {
  40.                                                 if (tagName.equals("low")) {
  41.                                                         //XML中的属性可以用下面的方法获取,其中0是序号,代表第一个属性
  42.                                                         tomorrowWeatherVO.setLow(parser.getAttributeValue(0));
  43.                                                 }
  44.                                                 if (tagName.equals("high")) {
  45.                                                         tomorrowWeatherVO.setHigh(parser.getAttributeValue(0));
  46.                                                 }
  47.                                                 if (tagName.equals("icon")) {
  48.                                                         tomorrowWeatherVO.setIcon(parser.getAttributeValue(0));
  49.                                                 }
  50.                                                 if (tagName.equals("condition")) {
  51.                                                         Log.i("yao", "condition=" + parser.getAttributeValue(0));
  52.                                                         tomorrowWeatherVO.setCondition(parser.getAttributeValue(0));
  53.                                                 }
  54.                                         }
  55.                                         break;
  56.                                 case XmlPullParser.END_TAG:
  57.                                         break;
  58.                                 case XmlPullParser.END_DOCUMENT:
  59.                                         break;
  60.                                 }

  61.                                 //别忘了用next方法处理下一个事件,忘了的结果就成死循环#_#
  62.                                 eventType = parser.next();
  63.                         }

  64.                 } catch (XmlPullParserException e) {
  65.                         e.printStackTrace();
  66.                 } catch (IOException e) {
  67.                         e.printStackTrace();
  68.                 }

  69.                 return tomorrowWeatherVO;
  70.         }
  71. }
复制代码

  编译和运行结果和上面的项目一模一样,我也就不上图了。我们可以看到pull解析方式更简单直接些,代码也少一些,至少省了一个handler文件,不是吗。好了本讲就到这里,祝愉快。










本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

0

主题

8

帖子

19

安币

初级码农

Rank: 1

发表于 2011-8-1 15:39:31 | 显示全部楼层
弱弱地问一句,那个switch下用 if ,else if是否好一点?
最后多谢楼主的资料。

1

主题

182

帖子

73

安币

程序猿

Rank: 2

发表于 2011-9-22 14:58:11 | 显示全部楼层
很好,呵呵

1

主题

42

帖子

73

安币

程序猿

Rank: 2

QQ达人

发表于 2011-9-23 10:12:52 | 显示全部楼层
很好,很强大!!!

0

主题

15

帖子

10

安币

初级码农

Rank: 1

发表于 2011-9-26 15:41:36 | 显示全部楼层
太感谢了。。。

0

主题

40

帖子

20

安币

初级码农

Rank: 1

发表于 2011-10-13 22:12:38 | 显示全部楼层
写的不错,下载来学习学习

0

主题

47

帖子

35

安币

程序猿

Rank: 2

发表于 2011-10-24 23:30:43 | 显示全部楼层
很好,很强大!!!

0

主题

15

帖子

9

安币

初级码农

Rank: 1

发表于 2011-11-27 13:47:23 | 显示全部楼层
xml解析不是通用的么?
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

领先的中文移动开发者社区
18620764416
7*24全天服务
意见反馈:1294855032@qq.com

扫一扫关注我们

Powered by Discuz! X3.2© 2001-2019 Comsenz Inc.( 粤ICP备15117877号 )