【Android Developers Training】 81. 解析XML数据
注:本文翻譯自Google官方的Android Developers Training文檔,譯者技術一般,由于喜愛安卓而產生了翻譯的念頭,純屬個人興趣愛好。
原文鏈接:http://developer.android.com/training/basics/network-ops/xml.html
可擴展標記語言(XML)是一種將文檔編碼為機器可閱讀的形式的規則集合。XML是一種在互聯網中分享數據的比較流行的格式。那些頻繁更新內容的網站(如新的站點或者博客),經常會提供一個XML源,這樣外部程序就可以與內容變更保持同步。上傳及解析XML數據對于需要聯網的應用來說是一個很平常的任務。這節課將講解如何解析XML文檔并使用它們的數據。
一). 選擇一個解析器
我們推薦使用XmlPullParser,它是一個在Android上解析XML的一種比較有效及穩定的方法。歷史中Android有兩種實現該接口的方法:
- 通過XmlPullParserFactory.newPullParser()實現的KXmlParser。
- 通過Xml.newPullParser()實現的ExpatPullParser。
每一種選擇都是可以的。不過這里我們使用第二個例子。?
二). 分析源
解析源的第一步是決定哪些字段是你感興趣的。解析器會提取這些你感興趣的字段數據并把其余的忽略。
下面是在應用中被解析的源的一段摘錄。每一個到StackOverflow.com的推送都會在源中顯示為一個entry標簽,并包含若干entry子標簽:
<?xml version="1.0" encoding="utf-8"?> <feed xmlns="http://www.w3.org/2005/Atom" xmlns:creativeCommons="http://backend.userland.com/creativeCommonsRssModule" ..."> <title type="text">newest questions tagged android - Stack Overflow</title> ...<entry>...</entry><entry><id>http://stackoverflow.com/q/9439999</id><re:rank scheme="http://stackoverflow.com">0</re:rank><title type="text">Where is my data file?</title><category scheme="http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest/tags" term="android"/><category scheme="http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest/tags" term="file"/><author><name>cliff2310</name><uri>http://stackoverflow.com/users/1128925</uri></author><link rel="alternate" href="http://stackoverflow.com/questions/9439999/where-is-my-data-file" /><published>2012-02-25T00:30:54Z</published><updated>2012-02-25T00:30:54Z</updated><summary type="html"><p>I have an Application that requires a data file...</p></summary></entry><entry>...</entry> ... </feed>應用會提取會提取entry標簽及其子標簽:title,link和summary子標簽的數據。
三). 初始化解析器
下一步是初始化解析器,并啟動解析的步驟。在下面的代碼片段中,一個不處理命名空間的解析器被初始化,并使用InputStream作為參數。通過調用nextTag()開始解析的步驟,并激活readFeed()方法,該方法提取并處理應用感興趣的數據:
public class StackOverflowXmlParser {// We don't use namespacesprivate static final String ns = null;public List parse(InputStream in) throws XmlPullParserException, IOException {try {XmlPullParser parser = Xml.newPullParser();parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);parser.setInput(in, null);parser.nextTag();return readFeed(parser);} finally {in.close();}}... }四). 閱讀源
readFeed()方法執行一些工作來處理源。它尋找entry標簽作為開始遞歸處理的起始點。如果一個標簽不是entry標簽,那么就忽略它。一點整個源都被遞歸處理完了,readFeed()方法返回一個包含它從源中提取的字段的List(包含子數據成員)。該List被解析器返回。
private List readFeed(XmlPullParser parser) throws XmlPullParserException, IOException {List entries = new ArrayList();parser.require(XmlPullParser.START_TAG, ns, "feed");while (parser.next() != XmlPullParser.END_TAG) {if (parser.getEventType() != XmlPullParser.START_TAG) {continue;}String name = parser.getName();// Starts by looking for the entry tagif (name.equals("entry")) {entries.add(readEntry(parser));} else {skip(parser);}} return entries; }五). 解析XML
解析一個XML源的步驟如下:
-
- 為每個你感興趣的標簽創建“read”方法。例如,readEntry(),readTitle()等。解析器從輸入流中讀取標簽。當它遇到了名為entry,title,link或summary時,它會為標簽調用相應的方法。否則就略過該標簽。
- 為每個不同類型標簽提取數據并將解析器推進到下一個標簽的方法。例如:
- 對于title和summary標簽,解析器調用readText()。該方法提取通過調用parser.getText(),從這些標簽中提取數據。
- 對于link標簽,解析器首先確定該link是否是自己感興趣的,如果是的話就調用parser.getAttributeValue()來提取它的值。
- 對于entry標簽,解析器會調用readEntry()。該方法解析entry中的子標簽,并返回一個Entry對象,其中包含了數據成員:title,link和summary。
- 一個用以輔助的方法skip()。更多信息可以閱讀:Skip Tags You Don't Care About。
下列代碼片段展示了如何解析上述標簽:
public static class Entry {public final String title;public final String link;public final String summary;private Entry(String title, String summary, String link) {this.title = title;this.summary = summary;this.link = link;} }// Parses the contents of an entry. If it encounters a title, summary, or link tag, hands them off // to their respective "read" methods for processing. Otherwise, skips the tag. private Entry readEntry(XmlPullParser parser) throws XmlPullParserException, IOException {parser.require(XmlPullParser.START_TAG, ns, "entry");String title = null;String summary = null;String link = null;while (parser.next() != XmlPullParser.END_TAG) {if (parser.getEventType() != XmlPullParser.START_TAG) {continue;}String name = parser.getName();if (name.equals("title")) {title = readTitle(parser);} else if (name.equals("summary")) {summary = readSummary(parser);} else if (name.equals("link")) {link = readLink(parser);} else {skip(parser);}}return new Entry(title, summary, link); }// Processes title tags in the feed. private String readTitle(XmlPullParser parser) throws IOException, XmlPullParserException {parser.require(XmlPullParser.START_TAG, ns, "title");String title = readText(parser);parser.require(XmlPullParser.END_TAG, ns, "title");return title; }// Processes link tags in the feed. private String readLink(XmlPullParser parser) throws IOException, XmlPullParserException {String link = "";parser.require(XmlPullParser.START_TAG, ns, "link");String tag = parser.getName();String relType = parser.getAttributeValue(null, "rel"); if (tag.equals("link")) {if (relType.equals("alternate")){link = parser.getAttributeValue(null, "href");parser.nextTag();} }parser.require(XmlPullParser.END_TAG, ns, "link");return link; }// Processes summary tags in the feed. private String readSummary(XmlPullParser parser) throws IOException, XmlPullParserException {parser.require(XmlPullParser.START_TAG, ns, "summary");String summary = readText(parser);parser.require(XmlPullParser.END_TAG, ns, "summary");return summary; }// For the tags title and summary, extracts their text values. private String readText(XmlPullParser parser) throws IOException, XmlPullParserException {String result = "";if (parser.next() == XmlPullParser.TEXT) {result = parser.getText();parser.nextTag();}return result; }... }六). 跳過你不關注的標簽
上面所描述的解析XML步驟中,其中有一步是解析器跳過我們不關注的標簽。下面是skip()方法的代碼:
private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {if (parser.getEventType() != XmlPullParser.START_TAG) {throw new IllegalStateException();}int depth = 1;while (depth != 0) {switch (parser.next()) {case XmlPullParser.END_TAG:depth--;break;case XmlPullParser.START_TAG:depth++;break;}}}它為何這樣就能實現跳過的功能呢:
- 如果當前遇到的不是START_TAG,那么拋出一個異常。
- 它接收START_TAG,以及之后遇到的內容,并匹配END_TAG。
- 為了確保它在正確的END_TAG停止,而不是在START_TAG之后遇到的第一個標簽,它會一直向子標簽深度搜索。
因此如果當前標簽含有子標簽,那么depth的值不會變成0,直到解析器處理了所有在原始的START_TAG和與它匹配的END_TAG之間的所有標簽。例如,考慮該解析器如何略過<author>標簽,該標簽含有兩個子標簽<name>和<uri>:
- 第一次while循環,解析器在<author>之后遇到了START_TAG:<name>,此時depth的值增加到2。
- 第二次while循環,解析器遇到了END_TAG:</name>。此時depth的值減少到1。
- 第三次while循環,解析器遇到了START_TAG:<uri>。此時depth的值增加到2。
- 第四次while循環,解析器遇到了END_TAG:</uri>。此時depth的值減少到1。
- 最后一次while循環,解析器遇到了END_TAG:</author>。此時depth的值減少到0,表明<author>已經被成功忽略了。
七). 處理XML數據
樣例代碼中,使用了AsyncTask獲取并解析XML源。這樣該過程就不會再UI主線程中執行。當處理執行完畢,應用會更新主Activity(NetworkActivity)的UI。
在下面摘錄的代碼片段中,loadPage()方法進行了如下的處理:
用XML源的URL初始化一個String變量。
在用戶的設置及網絡連接允許的情況下,調用new DownloadXmlTask().execute(url)。這將初始化一個新的DownloadXmlTask對象(AsyncTask的子類)并運行它的execute()方法,它會下載并解析源并將結果以String的形式返回,顯示在UI上。?
public class NetworkActivity extends Activity {public static final String WIFI = "Wi-Fi";public static final String ANY = "Any";private static final String URL = "http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest";// Whether there is a Wi-Fi connection.private static boolean wifiConnected = false; // Whether there is a mobile connection.private static boolean mobileConnected = false;// Whether the display should be refreshed.public static boolean refreshDisplay = true; public static String sPref = null;...// Uses AsyncTask to download the XML feed from stackoverflow.com.public void loadPage() { if((sPref.equals(ANY)) && (wifiConnected || mobileConnected)) {new DownloadXmlTask().execute(URL);}else if ((sPref.equals(WIFI)) && (wifiConnected)) {new DownloadXmlTask().execute(URL);} else {// show error } }AsyncTask的子類:DownloadXmlTask如下所示,它實現了下列AsyncTask的方法:
doInBackground()執行loadXmlFromNetwork(),它將源的URL作為參數傳入。loadXmlFromNetwork()方法獲取并處理源。當它結束以后,它會返回String作為結果。
onPostExecute()接收結果String并將它顯示在UI上。
// Implementation of AsyncTask used to download XML feed from stackoverflow.com. private class DownloadXmlTask extends AsyncTask<String, Void, String> {@Overrideprotected String doInBackground(String... urls) {try {return loadXmlFromNetwork(urls[0]);} catch (IOException e) {return getResources().getString(R.string.connection_error);} catch (XmlPullParserException e) {return getResources().getString(R.string.xml_error);}}@Overrideprotected void onPostExecute(String result) { setContentView(R.layout.main);// Displays the HTML string in the UI via a WebViewWebView myWebView = (WebView) findViewById(R.id.webview);myWebView.loadData(result, "text/html", null);} }下面是方法:loadXmlFromNetwork(),它被DownloadXmlTask調用,它執行下列任務:
- 初始化一個StackOverflowXmlParser,它也創建一個裝載entry對象的List(entries),以及title,url,和summary,來存儲從XML源中相應字段里提取出的數據。
- 調用downloadUrl(),它獲取源并以InputStream的形式返回
- 使用StackOverflowXmlParser來解析InputStream。StackOverflowXmlParser會用源中的數據填充entries這個List。
- 處理List,并將源數據和HTML標記向結合。
- 返回HTML字符串,由AsyncTask的onPostExecute()方法將它顯示在主Activity UI上的。
轉載于:https://www.cnblogs.com/jdneo/p/3612892.html
總結
以上是生活随笔為你收集整理的【Android Developers Training】 81. 解析XML数据的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微信利用PHP创建自定义菜单的方法
- 下一篇: phoengap–node+websoc