【Android Developers Training】 81. 解析XML数据
注:本文翻譯自Google官方的Android Developers Training文檔,譯者技術(shù)一般,由于喜愛安卓而產(chǎn)生了翻譯的念頭,純屬個(gè)人興趣愛好。
原文鏈接:http://developer.android.com/training/basics/network-ops/xml.html
可擴(kuò)展標(biāo)記語(yǔ)言(XML)是一種將文檔編碼為機(jī)器可閱讀的形式的規(guī)則集合。XML是一種在互聯(lián)網(wǎng)中分享數(shù)據(jù)的比較流行的格式。那些頻繁更新內(nèi)容的網(wǎng)站(如新的站點(diǎn)或者博客),經(jīng)常會(huì)提供一個(gè)XML源,這樣外部程序就可以與內(nèi)容變更保持同步。上傳及解析XML數(shù)據(jù)對(duì)于需要聯(lián)網(wǎng)的應(yīng)用來(lái)說(shuō)是一個(gè)很平常的任務(wù)。這節(jié)課將講解如何解析XML文檔并使用它們的數(shù)據(jù)。
一). 選擇一個(gè)解析器
我們推薦使用XmlPullParser,它是一個(gè)在Android上解析XML的一種比較有效及穩(wěn)定的方法。歷史中Android有兩種實(shí)現(xiàn)該接口的方法:
- 通過(guò)XmlPullParserFactory.newPullParser()實(shí)現(xiàn)的KXmlParser。
- 通過(guò)Xml.newPullParser()實(shí)現(xiàn)的ExpatPullParser。
每一種選擇都是可以的。不過(guò)這里我們使用第二個(gè)例子。?
二). 分析源
解析源的第一步是決定哪些字段是你感興趣的。解析器會(huì)提取這些你感興趣的字段數(shù)據(jù)并把其余的忽略。
下面是在應(yīng)用中被解析的源的一段摘錄。每一個(gè)到StackOverflow.com的推送都會(huì)在源中顯示為一個(gè)entry標(biāo)簽,并包含若干entry子標(biāo)簽:
<?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>應(yīng)用會(huì)提取會(huì)提取entry標(biāo)簽及其子標(biāo)簽:title,link和summary子標(biāo)簽的數(shù)據(jù)。
三). 初始化解析器
下一步是初始化解析器,并啟動(dòng)解析的步驟。在下面的代碼片段中,一個(gè)不處理命名空間的解析器被初始化,并使用InputStream作為參數(shù)。通過(guò)調(diào)用nextTag()開始解析的步驟,并激活readFeed()方法,該方法提取并處理應(yīng)用感興趣的數(shù)據(jù):
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()方法執(zhí)行一些工作來(lái)處理源。它尋找entry標(biāo)簽作為開始遞歸處理的起始點(diǎn)。如果一個(gè)標(biāo)簽不是entry標(biāo)簽,那么就忽略它。一點(diǎn)整個(gè)源都被遞歸處理完了,readFeed()方法返回一個(gè)包含它從源中提取的字段的List(包含子數(shù)據(jù)成員)。該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
解析一個(gè)XML源的步驟如下:
-
- 為每個(gè)你感興趣的標(biāo)簽創(chuàng)建“read”方法。例如,readEntry(),readTitle()等。解析器從輸入流中讀取標(biāo)簽。當(dāng)它遇到了名為entry,title,link或summary時(shí),它會(huì)為標(biāo)簽調(diào)用相應(yīng)的方法。否則就略過(guò)該標(biāo)簽。
- 為每個(gè)不同類型標(biāo)簽提取數(shù)據(jù)并將解析器推進(jìn)到下一個(gè)標(biāo)簽的方法。例如:
- 對(duì)于title和summary標(biāo)簽,解析器調(diào)用readText()。該方法提取通過(guò)調(diào)用parser.getText(),從這些標(biāo)簽中提取數(shù)據(jù)。
- 對(duì)于link標(biāo)簽,解析器首先確定該link是否是自己感興趣的,如果是的話就調(diào)用parser.getAttributeValue()來(lái)提取它的值。
- 對(duì)于entry標(biāo)簽,解析器會(huì)調(diào)用readEntry()。該方法解析entry中的子標(biāo)簽,并返回一個(gè)Entry對(duì)象,其中包含了數(shù)據(jù)成員:title,link和summary。
- 一個(gè)用以輔助的方法skip()。更多信息可以閱讀:Skip Tags You Don't Care About。
下列代碼片段展示了如何解析上述標(biāo)簽:
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; }... }六). 跳過(guò)你不關(guān)注的標(biāo)簽
上面所描述的解析XML步驟中,其中有一步是解析器跳過(guò)我們不關(guān)注的標(biāo)簽。下面是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;}}}它為何這樣就能實(shí)現(xiàn)跳過(guò)的功能呢:
- 如果當(dāng)前遇到的不是START_TAG,那么拋出一個(gè)異常。
- 它接收START_TAG,以及之后遇到的內(nèi)容,并匹配END_TAG。
- 為了確保它在正確的END_TAG停止,而不是在START_TAG之后遇到的第一個(gè)標(biāo)簽,它會(huì)一直向子標(biāo)簽深度搜索。
因此如果當(dāng)前標(biāo)簽含有子標(biāo)簽,那么depth的值不會(huì)變成0,直到解析器處理了所有在原始的START_TAG和與它匹配的END_TAG之間的所有標(biāo)簽。例如,考慮該解析器如何略過(guò)<author>標(biāo)簽,該標(biāo)簽含有兩個(gè)子標(biāo)簽<name>和<uri>:
- 第一次while循環(huán),解析器在<author>之后遇到了START_TAG:<name>,此時(shí)depth的值增加到2。
- 第二次while循環(huán),解析器遇到了END_TAG:</name>。此時(shí)depth的值減少到1。
- 第三次while循環(huán),解析器遇到了START_TAG:<uri>。此時(shí)depth的值增加到2。
- 第四次while循環(huán),解析器遇到了END_TAG:</uri>。此時(shí)depth的值減少到1。
- 最后一次while循環(huán),解析器遇到了END_TAG:</author>。此時(shí)depth的值減少到0,表明<author>已經(jīng)被成功忽略了。
七). 處理XML數(shù)據(jù)
樣例代碼中,使用了AsyncTask獲取并解析XML源。這樣該過(guò)程就不會(huì)再UI主線程中執(zhí)行。當(dāng)處理執(zhí)行完畢,應(yīng)用會(huì)更新主Activity(NetworkActivity)的UI。
在下面摘錄的代碼片段中,loadPage()方法進(jìn)行了如下的處理:
用XML源的URL初始化一個(gè)String變量。
在用戶的設(shè)置及網(wǎng)絡(luò)連接允許的情況下,調(diào)用new DownloadXmlTask().execute(url)。這將初始化一個(gè)新的DownloadXmlTask對(duì)象(AsyncTask的子類)并運(yùn)行它的execute()方法,它會(huì)下載并解析源并將結(jié)果以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如下所示,它實(shí)現(xiàn)了下列AsyncTask的方法:
doInBackground()執(zhí)行l(wèi)oadXmlFromNetwork(),它將源的URL作為參數(shù)傳入。loadXmlFromNetwork()方法獲取并處理源。當(dāng)它結(jié)束以后,它會(huì)返回String作為結(jié)果。
onPostExecute()接收結(jié)果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調(diào)用,它執(zhí)行下列任務(wù):
- 初始化一個(gè)StackOverflowXmlParser,它也創(chuàng)建一個(gè)裝載entry對(duì)象的List(entries),以及title,url,和summary,來(lái)存儲(chǔ)從XML源中相應(yīng)字段里提取出的數(shù)據(jù)。
- 調(diào)用downloadUrl(),它獲取源并以InputStream的形式返回
- 使用StackOverflowXmlParser來(lái)解析InputStream。StackOverflowXmlParser會(huì)用源中的數(shù)據(jù)填充entries這個(gè)List。
- 處理List,并將源數(shù)據(jù)和HTML標(biāo)記向結(jié)合。
- 返回HTML字符串,由AsyncTask的onPostExecute()方法將它顯示在主Activity UI上的。
轉(zhuǎn)載于:https://www.cnblogs.com/jdneo/p/3612892.html
總結(jié)
以上是生活随笔為你收集整理的【Android Developers Training】 81. 解析XML数据的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 微信利用PHP创建自定义菜单的方法
- 下一篇: phoengap–node+websoc