Android開發(fā)筆記XML數據解析方法及優(yōu)缺點
何為XML數據
XML 指可擴展標記語言(eXtensible Markup Language)。
可擴展標記語言(英語:Extensible Markup Language,簡稱:XML)是一種標記語言,是從標準通用標記語言(SGML)中簡化修改出來的。 其最主要的功能就是為了方便數據的傳輸與交換。
在Android開發(fā)中,我們有時候也需要從服務器上獲取xml數據并加以解析
如何解析XML數據
1.Pull解析
這里我們根據谷歌官方的開發(fā)者文檔提供的方法,也是其推薦的方法來解析xml數據。
1.分析Feed中感興趣的標簽內容
例如:
<?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>
就是一份xml數據,其中兩個對應<>中的內容即為一個標簽中的內容,比如說entry標簽中嵌套的id標簽為:
<id>http://stackoverflow.com/q/9439999</id>
其內容即為http://stackoverflow.com/q/9439999
2.選擇解析器
為了解析xml數據,我們需要選擇一些解析器來幫助我們分析數據。
官方文檔中提到:
XmlPullParser,這是一種在 Android 上解析 XML 的高效且可維護的方式。以前,Android 有此接口的兩個實現(xiàn):
KXmlParser(通過 XmlPullParserFactory.newPullParser())。
ExpatPullParser(通過 Xml.newPullParser())。
任一選擇都可以。此部分中的示例使用 ExpatPullParser(通過 Xml.newPullParser())。
3.實例化解析器
前文提到過,有具體的兩種方式實例化解析器,分別是 用工廠類生成 或者 直接用實例生成。比如:
//工廠類生成: XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); XmlPullParser xmlPullParser = factory.newPullParser(); //實例生成: XmlPullParser xmlPullParser = Xml.newPullParser();
兩者任選其一即可。
4.然后我們可以具體配置一下解析器
public class StackOverflowXmlParser { private 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(); } } ... }
其中,parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);這一行是用來禁用名稱空間的,當然setFeature方法還可以用來配置解析器的其他一些參數,可以查看官方的文檔。
parser.setInput(in, null);是設置了具體的數據流和編碼格式,如果設置為null則使用系統(tǒng)默認的編碼。此處還可以只設置一個字節(jié)流(Reader),比如:
xmlPullParser.setInput(new StringReader(xmlData));
parser.nextTag();調用 nextTag() 開始解析過程 ,官方的文檔里是這樣描述的:
Call next() and return event if it is START_TAG or END_TAG otherwise throw an exception. It will skip whitespace TEXT before actual tag if any.
就是說,如果是START_TAG或END_TAG,則調用next()并返回事件,否則拋出異常。它將跳過實際標記之前的空白TEXT(如果有的話)。本質代碼:
int eventType = next(); if(eventType == TEXT && isWhitespace()) { // skip whitespace eventType = next(); } if (eventType != START_TAG && eventType != END_TAG) { throw new XmlPullParserException("expected start or end tag", this, null); } return eventType;
next()方法則是用來獲取下一個解析事件的。
5.創(chuàng)建具體的方法來解析數據
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 tag if (name.equals("entry")) { entries.add(readEntry(parser)); } else { skip(parser); } } return entries; }
這里一開始的require方法是用來測試條件的,它接受三個參數,分別是預期的事件類型,名稱空間,名稱。
它將測試當前事件是否屬于給定的類型,以及名稱空間和名稱是否匹配。Null將匹配任何名稱空間和任何名稱。如果測試未通過,則拋出異常。異常文本指示解析器位置、預期事件和不滿足需求的當前事件。
這里的next事件可能返回的兩個值,XmlPullParser.START_TAG 和 XmlPullParser.END_TAG 分別對應的是開始解析一個節(jié)點和完成一個節(jié)點的解析的標志。還有一個重要的標志是XmlPullParser.END_DOCUMENT,對應的是解析工作完成。
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; } } }
接下來則會進入到entry標簽中進行進一步的解析:
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; } ... }
這里官方示例寫的比較復雜,但是模塊化做的較好。這里創(chuàng)建了一個靜態(tài)內部類Entry來輔助組織并返回數據。 首先開始讀取后利用getName()方法來獲取節(jié)點的名稱,對于每個具體的節(jié)點,也分別寫了不同的方法來讀取。
- 我們首先來看最基本的讀取方法readText方法,這實際上是對解析器提供的getText方法的封裝,當解析的下一個事件為TEXT時,即利用getText方法將其內容返回出去。
- 接著其實其他的解析方法都大同小異,但都要根據標簽內部具體的內容來設計具體的解析邏輯,比如實例中比較特殊的readLink方法,其中有一個getAttributeValue方法,是根據名稱空間和具體的屬性名稱來獲取屬性值的,
比如上面的代碼:
String relType = parser.getAttributeValue(null, “rel”);
就是設置無名稱空間,獲取了Link標簽里rel的具體的值。
然后再根據rel的值來決定是否解析href的值。
2.簡單的Pull解析
上面的Pull解析未免太過繁瑣,在Android第一行代碼中也有較為簡單的解析:
要解析的數據:
<apps> <app> <id>1</id> <name>Google Maps</name> <version>1.0</version> </app> <app> <id>2</id> <name>Chrome</name> <version>2.1</version> </app> <app> <id>3</id> <name>Google Play</name> <version>2.3</version> </app> </apps>
具體的解析方法:
private void parseXMLWithPull(String xmlData){ try{ XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); XmlPullParser xmlPullParser = factory.newPullParser(); xmlPullParser.setInput(new StringReader(xmlData)); int eventType = xmlPullParser.getEventType(); String id = ""; String name = ""; String version = ""; while(eventType != XmlPullParser.END_DOCUMENT){ String nodeName = xmlPullParser.getName(); switch (eventType){ case XmlPullParser.START_TAG:{ if("id".equals(nodeName)){ id = xmlPullParser.nextText(); }else if("name".equals(nodeName)){ name = xmlPullParser.nextText(); }else if ("version".equals(nodeName)){ version = xmlPullParser.nextText(); } } break; case XmlPullParser.END_TAG: { if("app".equals(nodeName)){ Log.d("MainActivity","id is "+id); Log.d("MainActivity","name is "+name); Log.d("MainActivity","version is "+version); } } break; default: break; } eventType = xmlPullParser.next(); } }catch (Exception e){ e.printStackTrace(); } }
這里省去了一些繁瑣的檢測,名稱空間的設置等,并且用XmlPullParser.END_DOCUMENT作為判斷解析事件是否完成的標志。
3.SAX解析
SAX解析與Pull解析類似,是由事件驅動的。其采用流式解析,解析與讀取同步,讀到哪就解析到哪。
要用SAX解析,我們就需要用到接口ContentHandle,一般情況下,我們可以繼承系統(tǒng)自帶的DefaultDocument,并且重寫其中的五個方法。
這里我們解析上面2中簡單Pull解析中的數據,重寫DefaultHandle類:
public class MyHandle extends DefaultHandler { private final String TAG = "MainActivity"; private String nodeName; private StringBuilder id; private StringBuilder name; private StringBuilder version; @Override public void startDocument() throws SAXException{ id = new StringBuilder(); name = new StringBuilder(); version = new StringBuilder(); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException{ nodeName = localName; } @Override public void characters(char[] ch,int start,int length)throws SAXException{ if("id".equals(nodeName)){ id.append(ch,start,length); }else if("name".equals(nodeName)){ name.append(ch,start,length); }else if("version".equals(nodeName)){ version.append(ch,start,length); } } @Override public void endElement(String uri,String localName,String qName)throws SAXException{ if("app".equals(localName)){ Log.d(TAG,"id is "+ id.toString().trim()); Log.d(TAG,"name is "+name.toString().trim()); Log.d(TAG,"version is "+version.toString().trim()); id.setLength(0); name.setLength(0); version.setLength(0); } } @Override public void endDocument() throws SAXException{ super.endDocument(); } }
SAX解析類似于觸發(fā)器,每個方法都會在一個特定的時候被調用,startDocument方法
會在開始解析任務時執(zhí)行,startElement方法會在開始解析一個節(jié)點時執(zhí)行,characters是具體解析節(jié)點的過程,endElement方法會在一個節(jié)點解析完成時執(zhí)行,endDocument方法會在整個解析過程完成后執(zhí)行。
總的來說,SAX解析的模版比較固定,語義也比較清晰。
到此這篇關于Android開發(fā)筆記XML數據解析方法及優(yōu)缺點的文章就介紹到這了,更多相關Android XML數據解析內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Android發(fā)送GET與POST請求的DEMO詳解
本篇文章是對Android發(fā)送GET與POST請求的DEMO進行了詳細的分析介紹,需要的朋友參考下2013-06-06Android Service判斷設備聯(lián)網狀態(tài)詳解
本文主要介紹Android Service判斷聯(lián)網狀態(tài),這里提供了相關資料并附有示例代碼,有興趣的小伙伴可以參考下,幫助開發(fā)相關應用功能2016-08-08Android編程實現(xiàn)TextView部分顏色變動的方法
這篇文章主要介紹了Android編程實現(xiàn)TextView部分顏色變動的方法,實例分析了TextView設置指定位置的背景色與字體顏色的相關技巧,需要的朋友可以參考下2015-12-12AccessibilityService實現(xiàn)微信發(fā)紅包功能
這篇文章主要為大家詳細介紹了AccessibilityService實現(xiàn)微信發(fā)紅包功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-12-12