Android(安卓)SAX 解析XML的Demo
一.基础知识
在《Android XML解析学习——Sax方式》中我们学习了Android上使用Java的SAX进行XML解析的方式。而在Android平台上使用SAX解析XML,除了使用Java的API外,还可以使用Android SDK带的="EN-US">API来实现。Android SDK中和SAX解析相关的包为android.sax,在这个包中Android提供了都有的SAX API,使用它们可以更加方便的进行SAX方式的XML解析。
当xml文件中在不同的位置处有相同的元素标签名时,在相应的事件回调处理函数中往往就需要进行判断处理。比如USGS的xml形式的地震数据为:
[xhtml] view plain copy- <?xmlversion="1.0"?>
- <feedxmlns="http://www.w3.org/2005/Atom"xmlns:georss="http://www.georss.org/georss">
- <updated>2010-09-15T04:41:18Z</updated>
- <title>USGSM2.5+Earthquakes</title>
- <subtitle>Real-time,worldwideearthquakelistforthepastday</subtitle>
- <linkrel="self"href="http://earthquake.usgs.gov/earthquakes/catalogs/1day-M2.5.xml"mce_href="http://earthquake.usgs.gov/earthquakes/catalogs/1day-M2.5.xml"/>
- <linkhref="http://earthquake.usgs.gov/earthquakes/"mce_href="http://earthquake.usgs.gov/earthquakes/"/>
- <author><name>U.S.GeologicalSurvey</name></author>
- <id>http://earthquake.usgs.gov/</id>
- <icon>/favicon.ico</icon>
- <entry>
- <id>urn:earthquake-usgs-gov:ak:10078833</id>
- <title>M2.9,SouthernAlaska</title>
- <updated>2010-09-15T04:14:03Z</updated>
- <linkrel="alternate"type="text/html"href="http://earthquake.usgs.gov/earthquakes/recenteqsww/Quakes/ak10078833.php"mce_href="http://earthquake.usgs.gov/earthquakes/recenteqsww/Quakes/ak10078833.php"/>
- <summarytype="html">
- <!--[CDATA[<imgsrc="http://earthquake.usgs.gov/images/globes/60_-155.jpg"mce_src="http://earthquake.usgs.gov/images/globes/60_-155.jpg"alt="59.909°N153.124°W"align="left"hspace="20"/><p>Wednesday,September15,201004:14:03UTC<br>Tuesday,September14,201008:14:03PMatepicenter</p><p><strong>Depth</strong>:98.90km(61.45mi)</p>]]-->
- </summary>
- <georss:point>59.9094-153.1241</georss:point>
- <georss:elev>-98900</georss:elev>
- <categorylabel="Age"term="Pasthour"/>
- </entry>
- <entry>
- <!--还有entry条目,省略-->
- </entry>
- </feed>
我们看到在<entry>开始前就有<updated>、<title>和<link>等元素标签,而<entry></entry>元素中也包含这些标签名,在SAX解析时都会产生相应的事件,但我们实际需要处理的为<entry></entry>元素中的这些标签产生的事件,因此我们设置了一个变量
[java] view plain copy- privateBooleanstartEntryElementFlag=false;
来进行判断。对以上的xml数据来说,这样的处理还不会出现问题,但是如果需要解析一个更加复杂的XML文档,则类似的需要对不同位置处的相同标签名进行判断这样的处理可能会带来各种各样的Bug。
而使用android.sax包中的API来进行SAX方式的解析则不会有以上的问题。实际上使用android.sax包还有点结合了我们以后会详细介绍的DOM方式,首先获取需要解析部分的根元素,然后使用getChild方法获取具体的某个子元素,之后就可以为具体的元素设置事件处理的回调函数,比如为一个元素分别设置元素开始的事件处理setStartElementListener,元素包含的文本内容结束的事件处理setEndTextElementListener和元素结束的事件处理setEndElementListener。
概括的来说,android.sax包进行XML解析的过程为用DOM的方式获取具体位置处的元素,然后为其设置需要的事件处理函数。具体的实现我们可以看实例开发部分的代码。
Android SDK中提供的和XML相关的还有一个类:android.util.Xml,在该类中提供了比较实用的XML相关的类方法,比如开始解析的parse方法,和直接创建XmlPullParser及XmlSerializer(这两块内容以后介绍)的方法等。
下面我们就用上面介绍的Android SDK中的SAX方式来实现解析XML形式的USGS地震数据的Demo例子。
二.实例开发
我们要完成的效果图如下图1所示:
图1 ListView列表显示的地震数据
和上一部分Demo例子的一样,也是解析完地震数据后用ListView列表的方式显示每条地震的震级和地名信息。
新建一个Android工程AndroidXMLDemoSaxII。
添加进上一个Demo工程AndroidXMLDemoSax中的EarthquakeEntry.java文件,如果需要从本地读取xml数据的话,同时在assets文件夹下添加保存为xml格式了的USGS地震数据USGS_Earthquake_1M2_5.xml,如果需要联网读取的话,在manifest.xml文件中添加权限:
[java] view plain copy- <uses-permissionandroid:name="android.permission.INTERNET"/>
并修改res/layout下的main.xml为:
[xhtml] view plain copy- <?xmlversion="1.0"encoding="utf-8"?>
- <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
- <ListView
- android:id="@+id/list"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- />
- </LinearLayout>
接下来就来新建添加一个类AndroidSaxEarthquakeHandler,以Android SDK提供的包android.sdk的API来完成解析地震数据的具体逻辑实现,内容如下:
[java] view plain copy- publicclassAndroidSaxEarthquakeHandler{
- //xml解析用到的Tag
- privateStringkRootElementName="feed";
- privateStringkEntryElementName="entry";
- privateStringkLinkElementName="link";
- privateStringkTitleElementName="title";
- privateStringkUpdatedElementName="updated";
- privateStringkGeoRSSPointElementName="point";
- privateStringkGeoRSSElevElementName="elev";
- staticfinalStringATOM_NAMESPACE="http://www.w3.org/2005/Atom";
- staticfinalStringGEORSS_NAMESPACE="http://www.georss.org/georss";
- //用于保存xml解析获取的结果
- privateArrayList<EarthquakeEntry>earthquakeEntryList;
- privateEarthquakeEntryearthquakeEntry;
- //解析xml数据
- publicArrayList<EarthquakeEntry>parse(InputStreaminStream)
- {
- earthquakeEntryList=newArrayList<EarthquakeEntry>();
- RootElementroot=newRootElement(ATOM_NAMESPACE,kRootElementName);
- Elemententry=root.getChild(ATOM_NAMESPACE,kEntryElementName);
- //具体解析xml
- //处理entry标签
- entry.setStartElementListener(newStartElementListener(){
- @Override
- publicvoidstart(Attributesattributes){
- //TODOAuto-generatedmethodstub
- earthquakeEntry=newEarthquakeEntry();
- }
- });
- entry.setEndElementListener(newEndElementListener(){
- @Override
- publicvoidend(){
- //TODOAuto-generatedmethodstub
- earthquakeEntryList.add(earthquakeEntry);
- }
- });
- //处理title标签
- entry.getChild(ATOM_NAMESPACE,kTitleElementName).setEndTextElementListener(newEndTextElementListener(){
- @Override
- publicvoidend(StringcurrentData){
- //TODOAuto-generatedmethodstub
- //提取强度信息
- StringmagnitudeString=currentData.split("")[1];
- intend=magnitudeString.length()-1;
- magnitudeString=magnitudeString.substring(0,end);
- doublemagnitude=Double.parseDouble(magnitudeString);
- earthquakeEntry.setMagnitude(magnitude);
- //提取位置信息
- Stringplace=currentData.split(",")[1].trim();
- earthquakeEntry.setPlace(place);
- }
- });
- //处理updated标签
- entry.getChild(ATOM_NAMESPACE,kUpdatedElementName).setEndTextElementListener(newEndTextElementListener(){
- @Override
- publicvoidend(StringcurrentData){
- //TODOAuto-generatedmethodstub
- //构造更新时间
- SimpleDateFormatsdf=newSimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
- Dateqdate=newGregorianCalendar(0,0,0).getTime();
- try{
- qdate=sdf.parse(currentData);
- }catch(ParseExceptione){
- e.printStackTrace();
- }
- earthquakeEntry.setDate(qdate);
- }
- });
- //处理point标签
- entry.getChild(GEORSS_NAMESPACE,kGeoRSSPointElementName).setEndTextElementListener(newEndTextElementListener(){
- @Override
- publicvoidend(StringcurrentData){
- //TODOAuto-generatedmethodstub
- //提取经纬度信息
- String[]latLongitude=currentData.split("");
- Locationlocation=newLocation("dummyGPS");
- location.setLatitude(Double.parseDouble(latLongitude[0]));
- location.setLongitude(Double.parseDouble(latLongitude[1]));
- earthquakeEntry.setLocation(location);
- }
- });
- //处理elev标签
- entry.getChild(GEORSS_NAMESPACE,kGeoRSSElevElementName).setEndTextElementListener(newEndTextElementListener(){
- @Override
- publicvoidend(StringcurrentData){
- //TODOAuto-generatedmethodstub
- //提取海拔高度信息
- doubleevel;
- //因为USGS数据有可能会输错,比如为"--10000",多了一个"-"号
- try{
- evel=Double.parseDouble(currentData);
- }catch(Exceptione){
- //TODO:handleexception
- e.printStackTrace();
- evel=0;
- }
- Log.v("Sax_Elev",String.valueOf(evel));
- earthquakeEntry.setElev(evel);
- }
- });
- //处理link标签
- entry.getChild(ATOM_NAMESPACE,kLinkElementName).setStartElementListener(newStartElementListener(){
- @Override
- publicvoidstart(Attributesattributes){
- //TODOAuto-generatedmethodstub
- //获取link链接
- StringwebLink=attributes.getValue("href");
- earthquakeEntry.setLink(webLink);
- }
- });
- //调用android.util.Xml开始解析
- try{
- Xml.parse(inStream,Xml.Encoding.UTF_8,root.getContentHandler());
- }catch(Exceptione){
- //TODOAuto-generatedcatchblock
- e.printStackTrace();
- }
- returnearthquakeEntryList;
- }
- }
开头处定义了解析需要的元素标签名称,因为getChild方法获取子元素时需要命名空间,因此还新定义了USGS的xml数据中包含的两个命名空间:
[java] view plain copy- staticfinalStringATOM_NAMESPACE="http://www.w3.org/2005/Atom";
- staticfinalStringGEORSS_NAMESPACE="http://www.georss.org/georss";
在定义的用于解析xml数据的方法中
publicArrayList<EarthquakeEntry>parse(InputStream inStream)
首先获取xml文档的根元素:
RootElement root =newRootElement(ATOM_NAMESPACE,kRootElementName);
有了根元素之后,就可以使用类似DOM的getChild方法获取具体的某个位置处的子元素,并且可以为具体的子元素注册事件处理器和在对应的回调函数中实现具体的处理逻辑。
从程序中我们可以看到,我们只为<entry>元素和<entry></entry>包含的子元素注册了事件处理器,因此即使xml文档开始处有<updated>、<title>和<link>等同名的元素标签,但也不会进行处理。因此和上一部分的Demo相比,就不再需要设置标志变量用来判断,而且看起来也更加简单了。
虽然写法不同了,但是对具体标签的处理逻辑和上一部分Demo中是一样的,因为处理的xml文档内容没有变。
程序的最后调用android.util.Xml类的类方法parse直接进行解析,也更加方便了。
[java] view plain copy- //调用android.util.Xml开始解析
- try{
- Xml.parse(inStream,Xml.Encoding.UTF_8,root.getContentHandler());
- }catch(Exceptione){
- //TODOAuto-generatedcatchblock
- e.printStackTrace();
- }
其中Xml类parse方法的ContentHandler参数由根元素通过getContentHandler()方式获得。
最后添加AndroidXMLDemoSaxII.java文件中的内容,内容和上一个Demo工程AndroidXMLDemoSax中的AndroidXMLDemoSax.java基本一样,
[java] view plain copy- publicclassAndroidXMLDemoSaxIIextendsActivity{
- /**Calledwhentheactivityisfirstcreated.*/
- //定义显示的List相关变量
- ListViewlist;
- ArrayAdapter<EarthquakeEntry>adapter;
- ArrayList<EarthquakeEntry>earthquakeEntryList;
- @Override
- publicvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- //获取地震数据流
- InputStreamearthquakeStream=readEarthquakeDataFromFile();
- //AndroidSax方式进行解析
- AndroidSaxEarthquakeHandlerandroidSaxHandler=newAndroidSaxEarthquakeHandler();
- earthquakeEntryList=androidSaxHandler.parse(earthquakeStream);
- //用ListView进行显示
- list=(ListView)this.findViewById(R.id.list);
- adapter=newArrayAdapter<EarthquakeEntry>(this,android.R.layout.simple_list_item_1,earthquakeEntryList);
- list.setAdapter(adapter);
- }
- privateInputStreamreadEarthquakeDataFromFile()
- {
- //从本地获取地震数据
- InputStreaminStream=null;
- try{
- inStream=this.getAssets().open("USGS_Earthquake_1M2_5.xml");
- }catch(IOExceptione){
- //TODOAuto-generatedcatchblock
- e.printStackTrace();
- }
- returninStream;
- }
- privateInputStreamreadEarthquakeDataFromInternet()
- {
- //从网络上获取实时地震数据
- URLinfoUrl=null;
- InputStreaminStream=null;
- try{
- infoUrl=newURL("http://earthquake.usgs.gov/earthquakes/catalogs/1day-M2.5.xml");
- URLConnectionconnection=infoUrl.openConnection();
- HttpURLConnectionhttpConnection=(HttpURLConnection)connection;
- intresponseCode=httpConnection.getResponseCode();
- if(responseCode==HttpURLConnection.HTTP_OK)
- {
- inStream=httpConnection.getInputStream();
- }
- }catch(MalformedURLExceptione){
- //TODOAuto-generatedcatchblock
- e.printStackTrace();
- }catch(IOExceptione){
- //TODOAuto-generatedcatchblock
- e.printStackTrace();
- }
- returninStream;
- }
- }
只是把进行SAX解析的部分换成了如下方式:
[java] view plain copy- //AndroidSax方式进行解析
- AndroidSaxEarthquakeHandlerandroidSaxHandler=newAndroidSaxEarthquakeHandler();
- earthquakeEntryList=androidSaxHandler.parse(earthquakeStream);
完成了,可以保存运行看下效果。
三.总结
Android平台提供了相当强大的XML解析支持,不仅包含了Java SDK中用来XML处理的API,而且Android SDK还提供了特有的用于SAX解析XML的包android.sax。使用这个包中的API可以更加方便解析,特别是当要解析的xml文档中在不同的层级位置处有多个相同名称的标签但需要分别进行不同处理时,同时也有更好的鲁棒性,减少解析时产生Bug的可能性。
以上我们介绍的都是SAX方式解析XML,而解析XML常用的还有DOM方式,这部分内容我们以后接着学习。
更多相关文章
- 大话Android的消息机制(Handler、Looper、Message...)
- Android常见错误处理
- [置顶] Android官方文档系列(翻译)
- Android启动模式之singleTask解析
- ANDROID的MANIFEST.XML文件字段解析
- Android启动过程深入解析
- android layout属性
- android横屏竖屏处理
- Android图案密码,手势锁源码解析