一. 簡單介紹
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 ?
- <?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" mce_href="http://earthquake.usgs.gov/earthquakes/catalogs/1day-M2.5.xml"/>
- <link href="http://earthquake.usgs.gov/earthquakes/" mce_href="http://earthquake.usgs.gov/earthquakes/"/>
- <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" mce_href="http://earthquake.usgs.gov/earthquakes/recenteqsww/Quakes/ak10078833.php"/>
- <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°N 153.124°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>
<?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°N 153.124°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 所示:

圖1 ListView 清單顯示的地震資料
解析完地震資料後用ListView 清單的方式顯示每條地震的震級和地名資訊。
建立一個Android 工程 AndroidXMLDemoSax 。
首先建立添加一個類EarthquakeEntry ,用來儲存一條地震資訊,類的内容為:
[java] view plain copy print ?
- 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;
- }
- }
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 ?
- 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);
- }
- }
- }
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 ?
- <?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>
<?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 ?
- 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;
- }
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 ?
- 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;
- }
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 ?
- //擷取地震資料流
- 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();
- }
//擷取地震資料流
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 ?
- //定義顯示的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);
//定義顯示的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 ?
- //使用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();
//使用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