在iOS開發(fā)中,大多數(shù)情況下,從網(wǎng)絡(luò)獲取的數(shù)據(jù)通常分兩種肴沫。
JSON格式或者XML格式。
JSON是一種輕量級(jí)的數(shù)據(jù)格式蕴忆,一般用于數(shù)據(jù)交互
JSON數(shù)據(jù)類似OC中的字典颤芬,解析方式也有很多
ios5中apple增加了解析JSON的api:NSJSONSerialization
(性能最好)
下面是NSJSONSerialization常用的兩個(gè)方法
// JSON數(shù)據(jù) > OC對(duì)象
+ (id)JSONObjectWithData:(NSData *)data options:(NSJSONReadingOptions)opt error:(NSError **)error;
// OC對(duì)象 > JSON數(shù)據(jù)
+ (NSData *)dataWithJSONObject:(id)obj options:(NSJSONWritingOptions)opt error:(NSError **)error;
另外Github上關(guān)于解析JSON的框架也有很多,JSONKit套鹅、SBJson站蝠、TouchJSON、JSONModel等芋哭。
關(guān)于JSON解析不做贅述沉衣,本文主要講解XML解析。
雖然現(xiàn)在大部分公司中給我們的數(shù)據(jù)都是JSON减牺,但是不排除一些比較老的公司中還在使用XML格式的數(shù)據(jù)豌习,去年剛?cè)胄械臅r(shí)候接到的第一個(gè)項(xiàng)目中數(shù)據(jù)就是XML格式的,數(shù)據(jù)量又大又復(fù)雜拔疚,其中一個(gè)模塊返回的XML數(shù)據(jù)接近1M肥隆,當(dāng)時(shí)真的是被虐成狗。
<h3>XML概述</h3>
XML全稱是Extensible Markup Language稚失,譯作“可擴(kuò)展標(biāo)記語(yǔ)言”
跟JSON一樣栋艳,也是常用的一種用于交互的數(shù)據(jù)格式
一般也叫XML文檔(XML Document)
一個(gè)常見的XML文檔一般由元素(Element)
和屬性(Attribute)
組成
一個(gè)元素包括了開始標(biāo)簽和結(jié)束標(biāo)簽
擁有元素內(nèi)容:<city>上海</city>
沒有元素內(nèi)容:<city></city>
沒有元素內(nèi)容的簡(jiǎn)寫:<city/>
一個(gè)元素可以嵌套若干個(gè)子元素(不能出現(xiàn)交叉嵌套)
<citys>
<city>
<name>上海</name>
<weather>大暴雨</weather>
<air>舒適</air>
</city>
</citys>
規(guī)范的XML文檔最多只有1個(gè)根元素,其他元素都是根元素的子孫元素
XML中的所有空格和換行句各,都會(huì)當(dāng)做具體內(nèi)容處理
一個(gè)元素可以擁有多個(gè)屬性吸占,屬性值必須用 雙引號(hào)"" 或者 單引號(hào)'' 括住。
<city name="上海" weather="大暴雨" air="舒適" />
屬性表示的信息也可以用子元素來(lái)表示凿宾,比如
<city>
<weather>大暴雨</weather>
<air>舒適</air>
</city>
關(guān)于XML具體的語(yǔ)法請(qǐng)百度or谷歌矾屯。
<h3>XML解析方式</h3>
DOM解析:一次性將整個(gè)XML文檔加載進(jìn)內(nèi)存,比較適合解析小文件
SAX解析:從根元素開始初厚,按順序一個(gè)元素一個(gè)元素往下解析件蚕,比較適合解析大文件
iOS SDK 提供了兩個(gè)xml框架。
-
NSXMLParser
:它是基于objective-c語(yǔ)言的sax解析框架产禾,是ios sdk默認(rèn)的xml解析框架排作,不支持dom模式。 - libxml2: 它是基于c語(yǔ)言的xml解析器亚情,被蘋果整合在ios sdk中妄痪,支持sax和dom模式。
第三方xml解析框架
- tbxml:它是輕量級(jí)的dom模式解析庫(kù)楞件,不支持xml文檔驗(yàn)證和xpath,只能讀取xml文檔拌夏,不能寫xml文檔僧著。
- touchxml:它是基于dom模式的解析庫(kù),與 tbxml類似障簿,只能讀取xml文檔,不能寫xml文檔栅迄。
- kissxml:它是基于dom模式的解析庫(kù)站故,基于touchxml,主要的不同是可以寫入xml文檔。
-
Gdataxml
:它是基于dom模式的解析庫(kù)毅舆,由google開發(fā)西篓,可以讀寫xml文檔,支持xpath查詢憋活。
<h3>具體解析實(shí)例</h3>
通過標(biāo)題岂津,應(yīng)該知道本文講解的是NSXMLParser解析與Gdataxml解析。
我從之前項(xiàng)目中截取一段數(shù)據(jù)作為例子分別使用兩種方式解析
<CrReport xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://tempuri.org/">
<CrRptByHour>
<CrReportByHour>
<Hour>10</Hour>
<Caption>10-12</Caption>
<CrChannel>0.8</CrChannel>
<CrRegion>0.6</CrRegion>
<CrArea>0.7</CrArea>
</CrReportByHour>
<CrReportByHour>
<Hour>12</Hour>
<Caption>12-14</Caption>
<CrChannel>1.5</CrChannel>
<CrRegion>0.9</CrRegion>
<CrArea>1.0</CrArea>
</CrReportByHour>
</CrRptByHour>
<CrRptByDay>
<CrReportByDay>
<Date>2015-03-02T00:00:00+08:00</Date>
<WeekDay>1</WeekDay>
<CrChannel>0.0330</CrChannel>
<CrRegion>0.0290</CrRegion>
<CrArea>0.0290</CrArea>
</CrReportByDay>
<CrReportByDay>
<Date>2015-03-02T00:00:00+08:00</Date>
<WeekDay>2</WeekDay>
<CrChannel>0.0310</CrChannel>
<CrRegion>0.0280</CrRegion>
<CrArea>0.0300</CrArea>
</CrReportByDay>
</CrRptByDay>
<CrRptSubTotal>
<CrReportSubTotal>
<RegionType>0</RegionType>
<Caption>SA Channel</Caption>
<CrYTD>0.0310</CrYTD>
<CrQTD>0.0310</CrQTD>
<CrMTD>0.0290</CrMTD>
<CrWTD>0.0000</CrWTD>
<CrYersterday>0.0000</CrYersterday>
<CrToday>0</CrToday>
</CrReportSubTotal>
<CrReportSubTotal>
<RegionType>1</RegionType>
<Caption>SA North B</Caption>
<CrYTD>0.0280</CrYTD>
<CrQTD>0.0280</CrQTD>
<CrMTD>0.0280</CrMTD>
<CrWTD>0.0000</CrWTD>
<CrYersterday>0.0000</CrYersterday>
<CrToday>0</CrToday>
</CrReportSubTotal>
</CrRptSubTotal>
</CrReport>
<h4>GDataXML</h4>
要使用GDataXML悦即,先要對(duì)項(xiàng)目進(jìn)行一些配置.
1>導(dǎo)入libxml2動(dòng)態(tài)庫(kù)
targets--Build Phases--link Binary With Libraries
2>設(shè)置libxml2的頭文件搜索路徑(為了能找到libxml2庫(kù)的所有頭文件)
在Head Search Path中加入/usr/include/libxml2
3>設(shè)置鏈接參數(shù)(自動(dòng)鏈接libxml2庫(kù))
在Other Linker Flags中加入-lxml2
4>由于GDataXML是非ARC的吮成,因此得設(shè)置編譯參數(shù)
CMD+B 編譯通過沒有報(bào)錯(cuò)說明環(huán)境配置成功。
GDataXML中常用的類
GDataXMLDocument: 代表整個(gè)XML文檔
GDataXMLElement: 代表文檔中的每個(gè)元素
使用attributeForName:方法可以獲得屬性值
上代碼(下面這段怎么編輯都有點(diǎn)問題辜梳,所以截圖了)
輸出內(nèi)容如下
你可以下載代碼試著解析CrRptByDay與CrRptSubTotal中的內(nèi)容粱甫。
GdataXML的最吸引人的在于他支持xpath語(yǔ)法,關(guān)于xpath語(yǔ)法內(nèi)容還還請(qǐng)各位看官另行搜索作瞄。我把之前項(xiàng)目中封裝的一整套解析工具也放到代碼中XML文件夾中茶宵,其中大量使用xpath,有興趣的朋友可以下載來(lái)研究一下宗挥。
另外完整數(shù)據(jù)涉及之前公司的商業(yè)機(jī)密乌庶,所有沒有放進(jìn)去,大家湊合看吧契耿。
<h4>NSXMLParser</h4>
NSXMLParser屬于SAX解析瞒大,是從上往下依次解析每個(gè)元素,在解析到每個(gè)元素的時(shí)候會(huì)通知代理宵喂,所以使用NSXMLParser必須遵守他的協(xié)議糠赦。
使用非常簡(jiǎn)單
// 創(chuàng)建解析器
NSXMLParser *parser = [[NSXMLParser alloc]initWithData:data];
// 設(shè)置代理
parser.delegate = self;
// 開始解析
[parser parse];
NSXMLParser的delegate
/**
* 解析到文檔的開頭時(shí)會(huì)調(diào)用
*/
- (void)parserDidStartDocument:(NSXMLParser *)parser
{
// NSLog(@"parserDidStartDocument----");
}
/**
* 解析到一個(gè)元素的開始就會(huì)調(diào)用
*
* @param elementName 元素名稱
* @param attributeDict 屬性字典
*/
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
一般情況,如果數(shù)據(jù)是這種格式,把內(nèi)容放到屬性中
<CrReportByHour Hour=@"10" Caption=@"10-12" CrChannel=@"0.8" CrRegion=@"0.6" CrArea=@"0.7"/>
系統(tǒng)會(huì)自動(dòng)把以上內(nèi)容轉(zhuǎn)為字典存放到attributeDict這個(gè)字典中
可以用KVC通過字典給模型直接復(fù)制(字典中的key必須都能在模型中找到)
}
/**
* 解析到一個(gè)元素的結(jié)束就會(huì)調(diào)用
*
* @param elementName 元素名稱
*/
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
// NSLog(@"didEndElement----%@", elementName);
}
/**
* 解析到文檔的結(jié)尾時(shí)會(huì)調(diào)用(解析結(jié)束)
*/
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
// NSLog(@"parserDidEndDocument----");
}
如果解析下面這段XML锅棕,直接在開始解析的方法中打印字典log輸出
<CrReport xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://tempuri.org/">
<CrRptByHour>
<CrReportByHour Hour="10" Caption="10-12" CrChannel="0.8" CrRegion="0.6" CrArea="0.7" />
<CrReportByHour Hour="10" Caption="10-14" CrChannel="1.5" CrRegion="0.9" CrArea="1.0" />
</CrRptByHour>
</CrReport>
看到熟悉的字典大家應(yīng)該懂了拙泽。
無(wú)奈當(dāng)時(shí)服務(wù)器返回的數(shù)據(jù)不是這種格式,而且內(nèi)容全都嵌入在元素中裸燎。
這里我們需要用到一個(gè)代理方法
// 當(dāng)解析器找到開始標(biāo)記和結(jié)束標(biāo)記之間的字符時(shí)顾瞻,調(diào)用這個(gè)方法解析當(dāng)前節(jié)點(diǎn)的所有字符
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
//string 就是每個(gè)元素中包含的內(nèi)容,我們需要在這里拿到并記錄自己需要的數(shù)據(jù)
self.currentString = string;
}
當(dāng)初由于項(xiàng)目需要在不同的地方展示這份XML中三分?jǐn)?shù)據(jù)德绿,所有我當(dāng)時(shí)的思路是在需要解析的地方創(chuàng)建解析工具類荷荤,傳入父元素名退渗,來(lái)獲取下面子節(jié)點(diǎn)的所有內(nèi)容.
根據(jù)自己傳入的元素名來(lái)判斷
在開始解析元素的方法中初始化可變字典,并且添加到全局的數(shù)組中
在上面的方法中用全局變量來(lái)記錄string值
在結(jié)束元素解析的方法中用用當(dāng)前元素作為key蕴纳,記錄的string為value寫入字典中会油。
XMLParser* parser = [[XMLParser alloc] parseDataByData:xmlData];
//傳入節(jié)點(diǎn)名稱獲取內(nèi)容 CrReportByHour、CrReportByDay古毛、CrReportSubTotal
NSMutableArray* array = [parser searchDataWithRootElement:@"CrReportByHour"];
NSLog(@"%@",array);
array的輸出內(nèi)容是
具體實(shí)現(xiàn)代碼大家下載來(lái)自己看吧翻翩,那時(shí)候剛?cè)胄校a寫的很渣稻薇。
代碼下載:https://github.com/hongfenglt/XMLParse