天天看点

Android XML解析学习——Sax方式 .

一. 简单介绍

XML( eX tensible Markup Language) , 即可扩展标记语言 , 是一种简单的数据存储语言,使用一系列简单的标记描述数据 。 XML 经常用作  Internet  上的一种数据格式 ,因此 如果您希望通过 Internet  访问数据,则数据很有可能是  XML  格式 ,或者 如果您希望发送数据给 Web  服务,那么您可能也需要发送  XML 。简而言之,如果您的  Android  应用程序将利用  Internet ,那么您可能需要使用  XML 。幸运的是,您可以采用多种方法在  Android  上使用  XML 。这个学习系列就和大家一起学习一下在 Android平台上读写 XML 数据的多种方式。

而最近用业余时间做了一个《地震及时通》,其中就需要从网络上读取实时的XML 形式的地震数据,因此我们在学习的同时将会完成读取 XML 形式的地震数据的 Demo 例子。

二. 基础知识

2.1 整体介绍

Android上对 XML 解析的支持是相当强大的,我们可以先来看一下 Android 中和 XML 解析相关的包:

1.   a ndroid.sax

这是 Android  SDK 提供的sax 解析的包,因为可以对具体的 Element 设置监听进行处理,因此有更好鲁棒性。

2.   a ndroid.util .Xml

这是 a ndroid.util 包中的其中一个类,提供 XML相关的实用方法,而且都是 public static 形式的类方法,即可以直接以类名调用。

3.  javax.xml.parsers

这是使用原来Java  SDK 用于xml 处理的 API ,即 JAXP( Java API for XML Processing ),主要提供了 SAX 和 DOM 方式解析 XML 的工厂方法。

4.  org.w3c.dom

提供具体的和DOM 方式解析 XML 相关的接口,如 Document 、 Element 等。

5.  org.xml.sax

提供具体的和SAX 方式解析 XML 相关的接口,如 XMLReader 及 4 个处理用的 Handler 等。

6.  org.xml.sax.helpers

提供SAX 的帮助类,以便更方便的用来解析,比如实现了 SAX 的 4 个处理用的 Handler 接口的 DefaultHandler ,用来更方便使用 XML 过滤器 XMLFilter 的 XMLFilterImpl ,和用于更方便创建 XMLReader的 XMLReaderFactory 等。

7.  org.xmlpull.v1

提供Pull 方式解析 XML 的接口 XmlPullParser 和用于写 XML 的 XmlSerializer 等。

以上就是Android 提供的和 XML 读写相关的一些包,在这个学习系列中我们将对这些包的功能进行具体的介绍,并依次使用这些 SAX 解析的方式完成读取 XML 地震数据的 Demo 例子。

2.2 SAX方式介绍

SAX ( Simple API for XML ) 是 基于 事件驱动的 XML  处理 模式, 主要是围绕着事件源以及事件处理器(或者叫监听器)来工作的。一个可以产生事件的对象被称为事件源,而可以针对事件产生响应的对象就被叫做事件处理器。事件源和事件处理器是通过在事件源中的事件处理器注册方法连接的。这样当事件源产生事件后 (比如碰到 XML元素的开始和结束等 ) ,调用事件处理器 (由许多回调函数组成) 相应的处理方法,一个事件就获得了处理。当然在事件源调用事件处理器中特定方法的时候,会传递给事件处理器相应事件的状态信息 (即回调函数中的参数) ,这样事件处理器才能够根据事件信息来决定自己的行为。

其中常用的事件处理回调函数有用于文档处理的文档开始: startDocument() ,文档结束: endDocument () , XML元素开始: startElement(String uri, String localName, String qName, Attributes attributes) , XML元素内容: characters( char [] ch,  int  start,  int  length) , XML元素结束: endElement(String uri, String localName, String qName) ,还有解析错误的回调函数 error ( SAXParseException  exception) 等。

在Android 系统中,提供了两种 SAX 解析的包,一种是原来 Java  SDK 就有的用于XML 处理的 API (称为 JAXP: Java API for XML Processing ,包含了 SAX和 DOM 两者相关的 API ),相关内容在包 javax.xml.parsers 中。还有一种是经过了 Android  SDK 包装了之后的sax 包,相关内容在包 android.sax 中。

这部分我们先来学习原来Java  SDK 就有的用SAX 方式处理 XML 的相关方法。在 javax.xml.parsers 包中,和 SAX 相关的为两个类: SAX 解析器工厂 SAXParserFactory 和 SAX解析器 SAXParser 。 SAXParserFactory 有 set方法和 get 方法 可以设置和获取一些配置选项,其中最重要的是调用 newSAXParser() 创建解析器 SAXParser 类的实例。 SAXParser 类包装了底层的 SAX解析器( org.xml.sax.XMLReader 的实例 ),即 SAXParser 实例调用 parse方法进行 XML 解析时,实际上会调用底层具体的 org.xml.sax 包中的 XMLReader。 SAXParser 实例也可以通过调用 getXMLReader()方法获得底层的 XMLReader 实例,一旦获得该实例,就可以按 XMLReader 方式使用更一般和具体的 SAX 方法。

通过以上的介绍我们知道 org.xml.sax 包是底层具体的负责 SAX解析相关的内容,并且为上层 javax.xml.parsers包提供 SAX 解析器等相关调用。下面我们就具体介绍一下用 SAX 进行解析的步骤。

在SAX 接口中,事件源是 org.xml.sax 包中的 XMLReader ,它通过 parse() 方法来开始解析 XML 文档并根据文档内容产生事件。而事件处理器则是 org.xml.sax 包中的 ContentHandler,DTDHandler,ErrorHandler, 以及  EntityResolver 这四个接口。它们分别处理事件源在解析过程中产生的不同种类的事件(其中 主要的为 ContentHandler ,处理和文档内容相关的事件 )。 而事件源XMLReader 和这四个事件处理器的连接是通过在 XMLReader 中的相应的事件处理器注册方法 set***() 来完成的。

因此概况一下具体步骤为:

1.  实现一个或多个处理器接口(ContentHandler, ErrorHandler, DTDHandler ,or EntityResover)

2.  创建一个XMLReader 类的实例

3.  在新的XMLReader 实例中通过大量的 set*****()  方法注册一个事件处理器的实例

4.  调用XMLReader 的 parse() 方法来处理文档 启动解析

以上部分的介绍是指使用 org.xml.sax包中提供的 SAX 解析的相关接口时的用法,但是一般常用并且比较方便的为使用 javax.xml.parsers 包提供的 SAX 工厂类 SAXParserFactory 创建 SAXParser 实例, 并且创建一个继承 org.xml.sax.helpers包中的 DefaultHandler 的类,用于实现具体的 SAX 事件的处理逻辑, DefaultHandler 类提供了 SAX 中 ContentHandler,DTDHandler,ErrorHandler,以及  EntityResolver 这四个接口 的所有回调方法默认的空实现,因此我们继承这个类后可以只覆盖我们需要的回调函数即可。然后调用 SAXParser 实例的 parse方法进行解析,用来解析的 xml 数据的形式可以为 InputStreams, Files, URLs, and SAX InputSources 等四种形式。

实现步骤和上面类似:

1.  在继承DefaultHandler 的类里面重写需要的回调函数

2.  创建 SAXParser 实例

3.  SAXParser 实例 调用parse 方法启动解析

下面我们就用上面介绍的 Java SDK 中的SAX 方式来实现解析 XML 形式的地震数据的 Demo 例子。

三. 实例开发

我们要解析的为美国地质调查局USGS 提供的地震数据, xml 数据地址为:

http://earthquake.usgs.gov/earthquakes/catalogs/1day-M2.5.xml

http://earthquake.usgs.gov/earthquakes/catalogs/7day-M2.5.xml

http://earthquake.usgs.gov/earthquakes/catalogs/7day-M5.xml

分别为1 天以内 2.5 级以上、 7 天内 2.5 级以上和 7 天内 5 级以上地震数据。

Xml数据的格式如下所示:

[xhtml] view plain copy print ?

  1. <?xml version="1.0"?>  
  2. <feed xmlns="http://www.w3.org/2005/Atom" xmlns:georss="http://www.georss.org/georss">  
  3.   <updated>2010-09-15T04:41:18Z</updated>  
  4.   <title>USGS M2.5+ Earthquakes</title>  
  5.   <subtitle>Real-time, worldwide earthquake list for the past day</subtitle>  
  6.   <link rel="self" href="http://earthquake.usgs.gov/earthquakes/catalogs/1day-M2.5.xml" mce_href="http://earthquake.usgs.gov/earthquakes/catalogs/1day-M2.5.xml"/>  
  7.   <link href="http://earthquake.usgs.gov/earthquakes/" mce_href="http://earthquake.usgs.gov/earthquakes/"/>  
  8.   <author><name>U.S. Geological Survey</name></author>  
  9.   <id>http://earthquake.usgs.gov/</id>  
  10.   <icon>/favicon.ico</icon>  
  11.   <entry>  
  12.       <id>urn:earthquake-usgs-gov:ak:10078833</id>  
  13.       <title>M 2.9, Southern Alaska</title>  
  14.       <updated>2010-09-15T04:14:03Z</updated>  
  15.       <link rel="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"/>  
  16.       <summary type="html">  
  17.         <!--[CDATA[<img src="http://earthquake.usgs.gov/images/globes/60_-155.jpg" mce_src="http://earthquake.usgs.gov/images/globes/60_-155.jpg" alt="59.909&#176;N 153.124&#176;W" align="left" hspace="20" /><p>Wednesday, September 15, 2010 04:14:03 UTC<br>Tuesday, September 14, 2010 08:14:03 PM at epicenter</p><p><b>Depth</b>: 98.90 km (61.45 mi)</p>]]-->  
  18.       </summary>  
  19.       <georss:point>59.9094 -153.1241</georss:point>  
  20.       <georss:elev>-98900</georss:elev>  
  21.       <category label="Age" term="Past hour"/>  
  22.   </entry>  
  23.    <entry>  
  24.     <!-- 还有entry条目,省略-->  
  25.   </entry>  
  26. </feed>  

<?xml version="1.0"?>

<feed xmlns="http://www.w3.org/2005/Atom" xmlns:georss="http://www.georss.org/georss">

<updated>2010-09-15T04:41:18Z</updated>

<title>USGS M2.5+ Earthquakes</title>

<subtitle>Real-time, worldwide earthquake list for the past day</subtitle>

<link rel="self" href="http://earthquake.usgs.gov/earthquakes/catalogs/1day-M2.5.xml" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" mce_href="http://earthquake.usgs.gov/earthquakes/catalogs/1day-M2.5.xml" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" />

<link href="http://earthquake.usgs.gov/earthquakes/" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" mce_href="http://earthquake.usgs.gov/earthquakes/" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" />

<author><name>U.S. Geological Survey</name></author>

<id>http://earthquake.usgs.gov/</id>

<icon>/favicon.ico</icon>

<entry>

<id>urn:earthquake-usgs-gov:ak:10078833</id>

<title>M 2.9, Southern Alaska</title>

<updated>2010-09-15T04:14:03Z</updated>

<link rel="alternate" type="text/html" href="http://earthquake.usgs.gov/earthquakes/recenteqsww/Quakes/ak10078833.php" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" mce_href="http://earthquake.usgs.gov/earthquakes/recenteqsww/Quakes/ak10078833.php" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" />

<summary type="html">

<!--[CDATA[<img src="http://earthquake.usgs.gov/images/globes/60_-155.jpg" mce_src="http://earthquake.usgs.gov/images/globes/60_-155.jpg" alt="59.909&#176;N 153.124&#176;W" align="left" hspace="20" /><p>Wednesday, September 15, 2010 04:14:03 UTC<br>Tuesday, September 14, 2010 08:14:03 PM at epicenter</p><p><b>Depth</b>: 98.90 km (61.45 mi)</p>]]-->

</summary>

<georss:point>59.9094 -153.1241</georss:point>

<georss:elev>-98900</georss:elev>

<category label="Age" term="Past hour"/>

</entry>

<entry>

<!-- 还有entry条目,省略-->

</entry>

</feed>

下面我们就来完成用Java SAX 的方式解析以上 XML 形式的 USGS 地震数据的 Android 例子。

我们要完成的效果图如下图1 所示:

Android XML解析学习——Sax方式 .

图1 ListView 列表显示的地震数据

解析完地震数据后用ListView 列表的方式显示每条地震的震级和地名信息。

新建一个Android 工程 AndroidXMLDemoSax 。

首先新建添加一个类EarthquakeEntry ,用来保存一条地震信息,类的内容为:

[java] view plain copy print ?

  1. public class EarthquakeEntry {  
  2.     //定义变量   
  3.     private Date date;  
  4.     private Location location;  
  5.     private String place;  
  6.     private String link;  
  7.     private double magnitude;  
  8.     private double elev;  
  9.     //构造函数   
  10.     public EarthquakeEntry()  
  11.     {  
  12.     }  
  13.     public EarthquakeEntry(Date _date, String _place, String _link, Location _location, double _magnitude, double _elev)  
  14.     {  
  15.         this.date = _date;  
  16.         this.location = _location;  
  17.         this.place = _place;  
  18.         this.link = _link;  
  19.         this.magnitude = _magnitude;  
  20.         this.elev = _elev;  
  21.     }     
  22.     //set方法   
  23.     public void setDate(Date _date)  
  24.     {  
  25.         this.date = _date;  
  26.     }  
  27.     public void setLocation(Location _location)  
  28.     {  
  29.         this.location = _location;  
  30.     }  
  31.     public void setPlace(String _place)  
  32.     {  
  33.         this.place = _place;  
  34.     }  
  35.     public void setLink(String _link)  
  36.     {  
  37.         this.link = _link;  
  38.     }  
  39.     public void setMagnitude(double _magnitude)  
  40.     {  
  41.         this.magnitude = _magnitude;  
  42.     }  
  43.     public void setElev(double _elev)  
  44.     {  
  45.         this.elev = _elev;  
  46.     }  
  47.     //get方法   
  48.     public Date getDate()  
  49.     {  
  50.         return this.date;  
  51.     }  
  52.     public Location getLocation()  
  53.     {  
  54.         return this.location;  
  55.     }  
  56.     public String getPlace()  
  57.     {  
  58.         return this.place;  
  59.     }  
  60.     public String getLink()  
  61.     {  
  62.         return this.link;  
  63.     }  
  64.     public double getMagnitude()  
  65.     {  
  66.         return this.magnitude;  
  67.     }  
  68.     public double getElev()  
  69.     {  
  70.         return this.elev;  
  71.     }  
  72.     @Override  
  73.     public String toString() {  
  74.         String earthquakeString = " M" + magnitude + " " + place;  
  75.         return earthquakeString;  
  76.     }  
  77.     }  

public class EarthquakeEntry {

//定义变量

private Date date;

private Location location;

private String place;

private String link;

private double magnitude;

private double elev;

//构造函数

public EarthquakeEntry()

{

}

public EarthquakeEntry(Date _date, String _place, String _link, Location _location, double _magnitude, double _elev)

{

this.date = _date;

this.location = _location;

this.place = _place;

this.link = _link;

this.magnitude = _magnitude;

this.elev = _elev;

}

//set方法

public void setDate(Date _date)

{

this.date = _date;

}

public void setLocation(Location _location)

{

this.location = _location;

}

public void setPlace(String _place)

{

this.place = _place;

}

public void setLink(String _link)

{

this.link = _link;

}

public void setMagnitude(double _magnitude)

{

this.magnitude = _magnitude;

}

public void setElev(double _elev)

{

this.elev = _elev;

}

//get方法

public Date getDate()

{

return this.date;

}

public Location getLocation()

{

return this.location;

}

public String getPlace()

{

return this.place;

}

public String getLink()

{

return this.link;

}

public double getMagnitude()

{

return this.magnitude;

}

public double getElev()

{

return this.elev;

}

@Override

public String toString() {

String earthquakeString = " M" + magnitude + " " + place;

return earthquakeString;

}

}

比较简单,定义和一条地震内容对应的变量,并设置set 和 get 函数,并且重写 toString ()函数在 ListView 输出时用。

接着新建添加一个类SaxEarthquakeHandler ,继承 DefaultHandler ,完成解析地震数据的具体逻辑实现,内容如下:

[java] view plain copy print ?

  1. public class SaxEarthquakeHandler extends DefaultHandler{  
  2.     //xml解析用到的Tag   
  3.     private String kEntryElementName = "entry";  
  4.     private String kLinkElementName = "link";  
  5.     private String kTitleElementName = "title";  
  6.     private String kUpdatedElementName = "updated";  
  7.     private String kGeoRSSPointElementName = "point";  
  8.     private String kGeoRSSElevElementName = "elev";  
  9.     //用于保存xml解析获取的结果   
  10.     private ArrayList<EarthquakeEntry> earthquakeEntryList;  
  11.     private EarthquakeEntry earthquakeEntry;  
  12.     private StringBuilder currentDataBuilder;  
  13.     private Boolean startEntryElementFlag = false;  
  14.     //获取解析的地震列表   
  15.     public ArrayList<EarthquakeEntry> getEarthquakeEntryList()  
  16.     {  
  17.         return this.earthquakeEntryList;  
  18.     }  
  19.     //具体的xml解析回调函数   
  20.     @Override  
  21.     public void startDocument() throws SAXException {  
  22.         super.startDocument();  
  23.         //在开始解析时先创建实例   
  24.         earthquakeEntryList = new ArrayList<EarthquakeEntry>();  
  25.         currentDataBuilder = new StringBuilder();  
  26.     }  
  27.     @Override  
  28.     public void endDocument() throws SAXException {  
  29.         // TODO Auto-generated method stub   
  30.         Log.v("Sax", "End");          
  31.     }  
  32.     //解析每一个标签Tag   
  33.     @Override  
  34.     public void startElement(String uri, String localName, String qName,  
  35.             Attributes attributes) throws SAXException {  
  36.         super.startElement(uri, localName, qName, attributes);  
  37. //      Log.v("Sax_StartElement", localName);   
  38.         if(localName.equalsIgnoreCase(kEntryElementName))  
  39.         {  
  40.             earthquakeEntry = new EarthquakeEntry();  
  41.             startEntryElementFlag = true;   
  42.         }  
  43.         else if ((localName.equalsIgnoreCase(kLinkElementName))&&(startEntryElementFlag == true)) {  
  44.             String relAttribute = attributes.getValue("rel");  
  45.             if(relAttribute.equalsIgnoreCase("alternate"))  
  46.             {  
  47.                 String webLink = attributes.getValue("href");  
  48.                 earthquakeEntry.setLink(webLink);  
  49.             }  
  50.         }  
  51.     }  
  52.     @Override  
  53.     public void characters(char[] ch, int start, int length)  
  54.             throws SAXException {  
  55.         super.characters(ch, start, length);  
  56.         currentDataBuilder.append(ch, start, length);  
  57.     }  
  58.     @Override  
  59.     public void endElement(String uri, String localName, String qName)  
  60.             throws SAXException {  
  61.         super.endElement(uri, localName, qName);  
  62.         if(startEntryElementFlag == true)  
  63.         {     
  64.             String currentData = currentDataBuilder.toString();  
  65.             if (localName.equalsIgnoreCase(kTitleElementName)) {  
  66.                 //提取强度信息   
  67.                 String magnitudeString = currentData.split(" ")[1];  
  68.                 int end =  magnitudeString.length()-1;  
  69.                 magnitudeString = magnitudeString.substring(0, end);  
  70.                 double magnitude = Double.parseDouble(magnitudeString);  
  71.                 earthquakeEntry.setMagnitude(magnitude);  
  72.                 //提取位置信息   
  73.                 String place = currentData.split(",")[1].trim();  
  74.                 earthquakeEntry.setPlace(place);  
  75.             }  
  76.             else if (localName.equalsIgnoreCase(kUpdatedElementName)) {  
  77.                 //构造更新时间   
  78.                 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");  
  79.                 Date qdate = new GregorianCalendar(0,0,0).getTime();  
  80.                 try {  
  81.                   qdate = sdf.parse(currentData);  
  82.                 } catch (ParseException e) {  
  83.                   e.printStackTrace();  
  84.                 }  
  85.                 earthquakeEntry.setDate(qdate);  
  86.             }  
  87.             else if (localName.equalsIgnoreCase(kGeoRSSPointElementName)) {  
  88.                 //提取经纬度信息   
  89.                 String[] latLongitude = currentData.split(" ");  
  90.                 Location location = new Location("dummyGPS");  
  91.                 location.setLatitude(Double.parseDouble(latLongitude[0]));  
  92.                 location.setLongitude(Double.parseDouble(latLongitude[1]));  
  93.                 earthquakeEntry.setLocation(location);  
  94.             }  
  95.             else if (localName.equalsIgnoreCase(kGeoRSSElevElementName)) {  
  96.                 //提取海拔高度信息   
  97.                 double evel;  
  98.                 //因为USGS数据有可能会输错,比如为"--10000",多了一个"-"号   
  99.                 try {  
  100.                     evel = Double.parseDouble(currentData);  
  101.                 } catch (Exception e) {  
  102.                     // TODO: handle exception   
  103.                     e.printStackTrace();  
  104.                     evel = 0;  
  105.                 }  
  106.                 Log.v("Sax_Elev", String.valueOf(evel));  
  107.                 earthquakeEntry.setElev(evel);  
  108.             }  
  109.             else if(localName.equalsIgnoreCase(kEntryElementName))  
  110.             {  
  111.                 earthquakeEntryList.add(earthquakeEntry);  
  112.                 startEntryElementFlag = false;  
  113.             }  
  114.             currentDataBuilder.setLength(0);  
  115.         }  
  116.     }     
  117.     }  

public class SaxEarthquakeHandler extends DefaultHandler{

//xml解析用到的Tag

private String kEntryElementName = "entry";

private String kLinkElementName = "link";

private String kTitleElementName = "title";

private String kUpdatedElementName = "updated";

private String kGeoRSSPointElementName = "point";

private String kGeoRSSElevElementName = "elev";

//用于保存xml解析获取的结果

private ArrayList<EarthquakeEntry> earthquakeEntryList;

private EarthquakeEntry earthquakeEntry;

private StringBuilder currentDataBuilder;

private Boolean startEntryElementFlag = false;

//获取解析的地震列表

public ArrayList<EarthquakeEntry> getEarthquakeEntryList()

{

return this.earthquakeEntryList;

}

//具体的xml解析回调函数

@Override

public void startDocument() throws SAXException {

super.startDocument();

//在开始解析时先创建实例

earthquakeEntryList = new ArrayList<EarthquakeEntry>();

currentDataBuilder = new StringBuilder();

}

@Override

public void endDocument() throws SAXException {

// TODO Auto-generated method stub

Log.v("Sax", "End");

}

//解析每一个标签Tag

@Override

public void startElement(String uri, String localName, String qName,

Attributes attributes) throws SAXException {

super.startElement(uri, localName, qName, attributes);

// Log.v("Sax_StartElement", localName);

if(localName.equalsIgnoreCase(kEntryElementName))

{

earthquakeEntry = new EarthquakeEntry();

startEntryElementFlag = true;

}

else if ((localName.equalsIgnoreCase(kLinkElementName))&&(startEntryElementFlag == true)) {

String relAttribute = attributes.getValue("rel");

if(relAttribute.equalsIgnoreCase("alternate"))

{

String webLink = attributes.getValue("href");

earthquakeEntry.setLink(webLink);

}

}

}

@Override

public void characters(char[] ch, int start, int length)

throws SAXException {

super.characters(ch, start, length);

currentDataBuilder.append(ch, start, length);

}

@Override

public void endElement(String uri, String localName, String qName)

throws SAXException {

super.endElement(uri, localName, qName);

if(startEntryElementFlag == true)

{

String currentData = currentDataBuilder.toString();

if (localName.equalsIgnoreCase(kTitleElementName)) {

//提取强度信息

String magnitudeString = currentData.split(" ")[1];

int end = magnitudeString.length()-1;

magnitudeString = magnitudeString.substring(0, end);

double magnitude = Double.parseDouble(magnitudeString);

earthquakeEntry.setMagnitude(magnitude);

//提取位置信息

String place = currentData.split(",")[1].trim();

earthquakeEntry.setPlace(place);

}

else if (localName.equalsIgnoreCase(kUpdatedElementName)) {

//构造更新时间

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");

Date qdate = new GregorianCalendar(0,0,0).getTime();

try {

qdate = sdf.parse(currentData);

} catch (ParseException e) {

e.printStackTrace();

}

earthquakeEntry.setDate(qdate);

}

else if (localName.equalsIgnoreCase(kGeoRSSPointElementName)) {

//提取经纬度信息

String[] latLongitude = currentData.split(" ");

Location location = new Location("dummyGPS");

location.setLatitude(Double.parseDouble(latLongitude[0]));

location.setLongitude(Double.parseDouble(latLongitude[1]));

earthquakeEntry.setLocation(location);

}

else if (localName.equalsIgnoreCase(kGeoRSSElevElementName)) {

//提取海拔高度信息

double evel;

//因为USGS数据有可能会输错,比如为"--10000",多了一个"-"号

try {

evel = Double.parseDouble(currentData);

} catch (Exception e) {

// TODO: handle exception

e.printStackTrace();

evel = 0;

}

Log.v("Sax_Elev", String.valueOf(evel));

earthquakeEntry.setElev(evel);

}

else if(localName.equalsIgnoreCase(kEntryElementName))

{

earthquakeEntryList.add(earthquakeEntry);

startEntryElementFlag = false;

}

currentDataBuilder.setLength(0);

}

}

}

首先定义了 xml解析和保存解析结果等相关的一些变量,接着定义一个 get 函数

//获取解析的地震列表

public  ArrayList<EarthquakeEntry> getEarthquakeEntryList()

{

return   this . earthquakeEntryList ;

}

返回解析的地震列表数据。

然后就是具体的xml 解析回调函数的重写实现,因为继承了类 DefaultHandler ,包含了 SAX 处理相关的 4 个 Handler 的所有回调函数的空实现,因此我们只需覆盖我们需要的回调函数。这是我们只重写了和文档内容处理相关的 ContentHandler 中 startDocument , endDocument , startElement , characters ,和 endElement 这几个回调函数,在实际应用中你可能还需添加错误处理或者其他的回调函数,只需重写覆盖即可。

回调函数中具体的处理逻辑和你的XML 数据的内容有关,以上实现了解析 USGS 的地震数据的处理逻辑,并添加了注释,如果有兴趣你可以对照着 USGS 的 XML 数据格式来看,这就不具体的讲了。

和地震相关的存储类和SAX 处理回调函数都完成了,接下来我们就来调用 SaxEarthquakeHandler 开始解析并显示数据。

先修改res/layout 下的 main.xml 为:

[xhtml] view plain copy print ?

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:orientation="vertical"  
  4.     android:layout_width="fill_parent"  
  5.     android:layout_height="fill_parent"  
  6.     >  
  7.   <ListView  
  8.     android:id="@+id/list"  
  9.     android:layout_width="fill_parent"   
  10.     android:layout_height="fill_parent"  
  11.   />  
  12.     </LinearLayout>  

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns: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>

添加了一个用于显示的ListView 控件。

接着添加AndroidXMLDemoSax.java 文件的内容。

先添加获取xml 数据源的方法:

[java] view plain copy print ?

  1. private InputStream readEarthquakeDataFromInternet()  
  2.     {  
  3.         //从网络上获取实时地震数据   
  4.         URL infoUrl = null;  
  5.         InputStream inStream = null;  
  6.         try {  
  7.             infoUrl = new URL("http://earthquake.usgs.gov/earthquakes/catalogs/1day-M2.5.xml");  
  8.             URLConnection connection = infoUrl.openConnection();  
  9.             HttpURLConnection httpConnection = (HttpURLConnection)connection;  
  10.             int responseCode = httpConnection.getResponseCode();  
  11.             if(responseCode == HttpURLConnection.HTTP_OK)  
  12.             {  
  13.                 inStream = httpConnection.getInputStream();  
  14.             }  
  15.         } catch (MalformedURLException e) {  
  16.             // TODO Auto-generated catch block   
  17.             e.printStackTrace();  
  18.         } catch (IOException e) {  
  19.             // TODO Auto-generated catch block   
  20.             e.printStackTrace();  
  21.         }  
  22.         return inStream;  
  23.         }  

private InputStream readEarthquakeDataFromInternet()

{

//从网络上获取实时地震数据

URL infoUrl = null;

InputStream inStream = null;

try {

infoUrl = new URL("http://earthquake.usgs.gov/earthquakes/catalogs/1day-M2.5.xml");

URLConnection connection = infoUrl.openConnection();

HttpURLConnection httpConnection = (HttpURLConnection)connection;

int responseCode = httpConnection.getResponseCode();

if(responseCode == HttpURLConnection.HTTP_OK)

{

inStream = httpConnection.getInputStream();

}

} catch (MalformedURLException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

return inStream;

}

这是从 USGS的网站上读取 XML 数据并以 InputStream  的形式返回。因为需要用到联网功能,所以还得在 manifest.xml文件中添加联网权限:

< uses-permission   android:name = "android.permission.INTERNET"   />

这是联网获取XML 数据,也可以从本地读取 XML 数据,因为校园网会打不开 USGS 的网站,因此复制了一份 USGS 的地震数据以文件的形式保存在 assets 文件夹下,并使用如下函数读取:

[java] view plain copy print ?

  1. private InputStream readEarthquakeDataFromFile()  
  2.     {  
  3.         //从本地获取地震数据   
  4.         InputStream inStream = null;  
  5.         try {  
  6.             inStream = this.getAssets().open("USGS_Earthquake_1M2_5.xml");  
  7.         } catch (IOException e) {  
  8.             // TODO Auto-generated catch block   
  9.             e.printStackTrace();  
  10.         }  
  11.         return inStream;  
  12.         }  

private InputStream readEarthquakeDataFromFile()

{

//从本地获取地震数据

InputStream inStream = null;

try {

inStream = this.getAssets().open("USGS_Earthquake_1M2_5.xml");

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

return inStream;

}

有了XML 数据,就可以接下来进行解析了。

[java] view plain copy print ?

  1.  //获取地震数据流   
  2.       InputStream earthquakeStream = readEarthquakeDataFromFile();  
  3.       //Sax方式进行xml解析   
  4.     SAXParserFactory factory = SAXParserFactory.newInstance();  
  5.     try {  
  6.     SAXParser parser = factory.newSAXParser();  
  7.     SaxEarthquakeHandler saxHandler = new SaxEarthquakeHandler();  
  8.     parser.parse(earthquakeStream, saxHandler);  
  9.     //获取解析后的列表数据   
  10.     earthquakeEntryList = saxHandler.getEarthquakeEntryList();  
  11. } catch (Exception e) {  
  12.     // TODO Auto-generated catch block   
  13.     e.printStackTrace();  
  14.     }  

//获取地震数据流

InputStream earthquakeStream = readEarthquakeDataFromFile();

//Sax方式进行xml解析

SAXParserFactory factory = SAXParserFactory.newInstance();

try {

SAXParser parser = factory.newSAXParser();

SaxEarthquakeHandler saxHandler = new SaxEarthquakeHandler();

parser.parse(earthquakeStream, saxHandler);

//获取解析后的列表数据

earthquakeEntryList = saxHandler.getEarthquakeEntryList();

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

最后添加定义相关变量,用把解析的数据用ListView 显示:

[java] view plain copy print ?

  1. //定义显示的List相关变量   
  2. ListView list;  
  3. ArrayAdapter<EarthquakeEntry> adapter;  
  4.     ArrayList<EarthquakeEntry> earthquakeEntryList;  
  5.     //用ListView进行显示   
  6.     list = (ListView)this.findViewById(R.id.list);  
  7.     adapter = new ArrayAdapter<EarthquakeEntry>(this, android.R.layout.simple_list_item_1, earthquakeEntryList);  
  8.         list.setAdapter(adapter);  

//定义显示的List相关变量

ListView list;

ArrayAdapter<EarthquakeEntry> adapter;

ArrayList<EarthquakeEntry> earthquakeEntryList;

//用ListView进行显示

list = (ListView)this.findViewById(R.id.list);

adapter = new ArrayAdapter<EarthquakeEntry>(this, android.R.layout.simple_list_item_1, earthquakeEntryList);

list.setAdapter(adapter);

完成了,可以保存运行看下效果。

以上使用的是javax.xml.parsers 包中的 SAXParser 来实现, SAXParser 包装了底层的 XMLReader ,实现起来更加方便。但是我们也可以使用 XMLReader 来实现解析,下面就看下使用具体的 XMLReader 的方式,实现代码如下所示:

[java] view plain copy print ?

  1.         //使用org.xml.sax包中的XMLReader进行xml解析   
  2.         XMLReader xmlReader = null;  
  3.         //获取XMLReader的方式有两种   
  4.         //方式一:使用javax.xml.parsers.SAXParser的getXMLReader()方法   
  5. //        try {   
  6. //          xmlReader = SAXParserFactory.newInstance().newSAXParser().getXMLReader();   
  7. //      } catch (Exception e) {   
  8. //          // TODO Auto-generated catch block   
  9. //          e.printStackTrace();   
  10. //      }   
  11.         //方式二:使用org.xml.sax.helpers.XMLReaderFactory的createXMLReader()方法   
  12.         try {  
  13.             //设置系统的"org.xml.sax.driver",XMLReaderFactory创建createXMLReader()时要用到   
  14.             System.setProperty("org.xml.sax.driver","org.xmlpull.v1.sax2.Driver");  
  15.             xmlReader = XMLReaderFactory.createXMLReader();  
  16.         } catch (SAXException e) {  
  17.             // TODO Auto-generated catch block   
  18.             e.printStackTrace();  
  19.         }  
  20.         SaxEarthquakeHandler saxHandler = new SaxEarthquakeHandler();  
  21.         xmlReader.setContentHandler(saxHandler);  
  22.         //XMLReader的输入为InputSource   
  23.         InputSource inSource = new InputSource(earthquakeStream);  
  24.         try {  
  25.             xmlReader.parse(inSource);  
  26.         } catch (Exception e) {  
  27.             // TODO Auto-generated catch block   
  28.             e.printStackTrace();  
  29.         }  
  30.         //获取解析后的列表数据   
  31.             earthquakeEntryList = saxHandler.getEarthquakeEntryList();  

//使用org.xml.sax包中的XMLReader进行xml解析

XMLReader xmlReader = null;

//获取XMLReader的方式有两种

//方式一:使用javax.xml.parsers.SAXParser的getXMLReader()方法

// try {

// xmlReader = SAXParserFactory.newInstance().newSAXParser().getXMLReader();

// } catch (Exception e) {

// // TODO Auto-generated catch block

// e.printStackTrace();

// }

//方式二:使用org.xml.sax.helpers.XMLReaderFactory的createXMLReader()方法

try {

//设置系统的"org.xml.sax.driver",XMLReaderFactory创建createXMLReader()时要用到

System.setProperty("org.xml.sax.driver","org.xmlpull.v1.sax2.Driver");

xmlReader = XMLReaderFactory.createXMLReader();

} catch (SAXException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

SaxEarthquakeHandler saxHandler = new SaxEarthquakeHandler();

xmlReader.setContentHandler(saxHandler);

//XMLReader的输入为InputSource

InputSource inSource = new InputSource(earthquakeStream);

try {

xmlReader.parse(inSource);

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

//获取解析后的列表数据

earthquakeEntryList = saxHandler.getEarthquakeEntryList();

其中获取XMLReader 的方式有两种,一种是先获取 SAXParser ,然后通过 getXMLReader() 方法来获取,

xmlReader = SAXParserFactory.newInstance().newSAXParser().getXMLReader();

另一种是直接使用XMLReader 的工厂类 XMLReaderFactory 调用 createXMLReader() 方法创建,

xmlReader = XMLReaderFactory. createXMLReader ();

但 不管是那种方式,只有获得了XMLReader 实例,接下来的操作都是一样的,先注册文档内容的事件处理器实例,

xmlReader.setContentHandler(saxHandler);

然后调用parse方法开始解析,

xmlReader.parse(inSource);

这样就用XMLReader进行了XML解析。

四. 总结

在这部分中我们首先学习了Android 上和 XML 解析相关的各个包的简单介绍,并且从有这么多个相关的包我们可以知道 Android 为 XML 的读写提供了相当大的支持。

然后具体学习了Android 上使用 SAX 方式解析 XML 的基本知识,使用 javax.xml.parsers 包中的 SAXParser 进行解析,及使用 org.xml.sax 包中的 XMLReader 进行解析两种方式分别的步骤,最后用解析 USGS 地震数据的 Demo 例子来实现介绍的内容。

这部分介绍的SAX 方式是属于原来 Java 就有的 XML 处理方式,同时, Android 平台为了使解析 XML 还能更加方便和更加健壮,提供了 android.sax 包来进行 SAX 进行 XML ,这部分内容我们以后我们继续接着学习。

注:

参考资料: http://www.ibm.com/developerworks/cn/xml/x-saxhandle