關(guān)于XML:
網(wǎng)絡(luò)應(yīng)用中的數(shù)據(jù)解析嚎莉,因?yàn)樽罱膽?yīng)用米酬,無論是Android的和ios平臺的,一直用也是建議用的都是Json解析趋箩,xml解析都有點(diǎn)被遺忘了赃额。
然后最近自己在做著玩一個ios的小應(yīng)用,涉及網(wǎng)絡(luò)數(shù)據(jù)的抓取叫确,一些網(wǎng)站可能都提供了自己api平臺跳芳,這些一般都是支持
我們對于數(shù)據(jù)協(xié)議格式的設(shè)定的。但是后來我在找尋到一些Rss資源時竹勉,發(fā)現(xiàn)返回的數(shù)據(jù)都是xml格式的飞盆,
因此,那就只好用xml解析了。
XML解析其實(shí)這個概念出現(xiàn)了算夠久了吓歇,以前javaweb什么到處都在用孽水。這邊我們主要大致介紹下,然后在在ios編程如何使用城看。
XML解析一般分兩種模式SAX和DOM女气,事件和文檔。具體解析google去吧析命,有詳細(xì)主卫。不過看了下面的兩個例子,一般就了解了.
一:XML解析之SAX解析鹃愤,以及對NSXMLParser的應(yīng)用
首先導(dǎo)入?yún)f(xié)議
<NSXMLParserDelegate>
XMLDemo.xml
<?xml version="1.0" encoding="utf-8"?> <!--此行包含XML的版本信息和編碼格式-->
<students><!--這是開始標(biāo)簽簇搅,也就是根節(jié)點(diǎn)-->
<student attribute = "四班神獸"><!--student為根節(jié)點(diǎn)的子節(jié)點(diǎn),name節(jié)點(diǎn)的父節(jié)點(diǎn)软吐, attribute是它的屬性-->
<name>洛洛受</name><!--洛洛受為name節(jié)點(diǎn)的值-->
<sex>無</sex>
<age>14</age>
</student>
<student attribute = "神獸之友">
<name>鄒杰</name>
<sex>隨神獸可隨時變化</sex>
<age>17</age>
</student>
</students><!--節(jié)點(diǎn)的結(jié)束標(biāo)簽都是以/加標(biāo)簽名稱組成 -->
讓我們深入的了解SAX解析
首先定義一個可變數(shù)組用來盛放獲取到的字符串
@property(nonatomic,retain)NSMutableString *mutableString; // 用來盛放獲取的字符串
// XML的sax解析
- (void)xmlSaxParser{
NSString *xmlPath = [[NSBundle mainBundle]pathForResource:@"XMLDemo.xml" ofType:nil];
NSData *data = [NSData dataWithContentsOfFile:xmlPath];
// 開始sax解析的學(xué)習(xí)
NSXMLParser *xmlParser = [[NSXMLParser alloc]initWithData:data];
// 由于sax解析是一個事件的處理過程瘩将,所以肯定是有順序的,所以需要借助代理來執(zhí)行解析過程
xmlParser.delegate = self;
// 開始解析凹耙, 返回值為BOOL類型姿现,解析的過程是一個同步,意思就是說肖抱,只要開始解析备典,解析未完成,它后面的代碼就不會執(zhí)行意述。
BOOL isParser = [xmlParser parse];
if (isParser) {
NSLog(@"解析成功");
}else{
NSLog(@"解析失敗");
}
NSLog(@"我是洛洛不是裸裸");
}
由于sax解析是一個事件的處理過程提佣,所以肯定是有順序的,所以需要借助代理來執(zhí)行解析過程,有5個代理荤崇,我自己寫了一個方法用來 處理標(biāo)簽中的空格拌屏,換行, \t等制表符 (自己寫的方法)
#pragma mark ----sax解析的代理方法
// 1.開始解析的時候,可以為容器進(jìn)行初始化 (準(zhǔn)備解析)
- (void)parserDidStartDocument:(NSXMLParser *)parser{
self.mutableString = nil;
NSLog(@"開始解析");
}
// 開始解析XML中的某個標(biāo)簽
/**
*
*
* @param parser :解析器
* @param elementName :節(jié)點(diǎn)名稱
* @param namespaceURI :命名空間中的URL
* @param qName :命名空間的名稱
* @param attributeDict :標(biāo)簽的屬性
*/
// 2.準(zhǔn)備解析節(jié)點(diǎn)
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary<NSString *,NSString *> *)attributeDict{
NSLog(@"開始解析標(biāo)簽-----%@---------%@",elementName,attributeDict);
}
// 3.從標(biāo)簽中取值 在foundCharacters:方法中一直保存當(dāng)前最新的值
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
if (self.mutableString == nil) {
self.mutableString = [NSMutableString stringWithString:string];
}else{
[self.mutableString appendString:string];
}
NSLog(@"從標(biāo)簽中取值------%@",string);
}
// 處理標(biāo)簽中的空格术荤,換行, \t等制表符 (自己寫的方法)
- (NSString *)replaceStringWithString:(NSMutableString *)string{
NSString *str1 = [string stringByReplacingOccurrencesOfString:@"\n" withString:@""];
NSString *str2 = [str1 stringByReplacingOccurrencesOfString:@" " withString:@""];
NSString *str3 = [str2 stringByReplacingOccurrencesOfString:@"\r" withString:@""];
NSString *str4 = [str3 stringByReplacingOccurrencesOfString:@"\t" withString:@""];
return str4;
}
// 5.某個標(biāo)簽取值結(jié)束
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
// 根據(jù)標(biāo)簽名稱來判斷當(dāng)前是哪個標(biāo)簽取值結(jié)束
if ([elementName isEqualToString:@"name"]) { // 說明name取值結(jié)束了
NSLog(@"name------%@",[self replaceStringWithString:self.mutableString]);
}
// 由于任意一個標(biāo)簽取值結(jié)束之后倚喂,可變字符串是重復(fù)利用的,所以需要重新置為nil瓣戚,讓它在取值代理方法中來保存
self.mutableString = nil;
NSLog(@"標(biāo)簽取值結(jié)束-------%@",elementName);
}
// 5.整個XML取值結(jié)束
- (void)parserDidEndDocument:(NSXMLParser *)parser{
NSLog(@"整個取值結(jié)束");
}
//獲取cdata塊數(shù)據(jù)
- (void)parser:(NSXMLParser *)parser foundCDATA:(NSData *)CDATABlock
{
// NSLog(@"%@",NSStringFromSelector(_cmd) );
}
1.初始化解析器端圈,傳入你要解析的數(shù)據(jù)。
2.parse子库,啟動解析枫笛,返回一個是否解析成功Bool值。
3.基本你要處理的就在下面實(shí)現(xiàn)的1-5個代理方法了刚照。
其實(shí)代理方法和詳細(xì),就是一個事物進(jìn)行流程:
step1是準(zhǔn)備解析喧兄,然后沒意外就是執(zhí)行到了——>
step2讀取到第一個頭節(jié)點(diǎn)了无畔,然后如果內(nèi)部有屬性值啊楚,你可以獲取出來,讀完頭節(jié)點(diǎn)浑彰,我們會進(jìn)去值域——》
step3對于簡單的節(jié)點(diǎn)恭理,可能直接就是一個string值了,但是看例子我們會知道郭变,很多情況下颜价,該節(jié)點(diǎn)的值域包含的于是一個節(jié)點(diǎn)——》
這步其實(shí)分兩種,如果是值诉濒,那么就是執(zhí)行step4周伦,獲取值的字符串,如果是子節(jié)點(diǎn)呢未荒,我們一看就知道专挪,它又是進(jìn)行了step2,
即讀取到頭標(biāo)簽了片排,其實(shí)你是很人讀一片文章流程一樣寨腔,只不過我們腦中有個印象<xxx>是頭標(biāo)簽了,我們要做什么率寡,讀到頭標(biāo)簽的最后一個符號">"
下面進(jìn)去值域迫卢,讀到了字符串的話就調(diào)用了foundCharacters:(NSString *)string,如果又讀到<xxx>這樣的冶共,那就又是頭標(biāo)簽了乾蛤。——》
step5就是讀到開始尾標(biāo)簽符號了比默。
最后一個方法
foundCDATA:(NSData *)CDATABlock幻捏,其實(shí)也是一個格式
二:Dom文檔解析模型,TBXML第三方包應(yīng)用命咐。(需要導(dǎo)入一個第三方的頭文件)
dom解析模型就像一個樹結(jié)構(gòu)篡九,節(jié)點(diǎn),子節(jié)點(diǎn)醋奠,兄弟節(jié)點(diǎn)等等榛臼。
這個其實(shí)最后被我拋棄了,這個解析器太簡化了窜司,太簡潔的東西導(dǎo)致控制的入口點(diǎn)太少沛善,就比如一個一鍵優(yōu)化的軟件的概念是一樣的,
一鍵清楚緩存塞祈,優(yōu)化配置金刁,文件歸類等等。人為控制就少了,導(dǎo)致我解析上面那個模型時尤蛮,只知道遍歷存儲~媳友。但是這個解析期對部分要求不高的xml解析其實(shí)挺好分,真的很簡潔产捞。
#import "GDataXMLNode.h"
#pragma mark - XML的dom解析方式
// XML的dom解析方式
- (void)xmlDomParser{
NSString *xmlPath = [[NSBundle mainBundle]pathForResource:@"XMLDemo.xml" ofType:nil];
NSData *data = [NSData dataWithContentsOfFile:xmlPath];
// 將XML文件讀取到內(nèi)存中醇锚,并轉(zhuǎn)為文檔模型,并且是樹狀結(jié)構(gòu)
NSError *error;
GDataXMLDocument *xmlDocument = [[GDataXMLDocument alloc]initWithData:data options:0 error:&error];
// dom解析的時候坯临,從根節(jié)點(diǎn)開始解析焊唬,如果只有一個節(jié)點(diǎn),那么咱們就直接取值看靠,如果此節(jié)點(diǎn)還有子節(jié)點(diǎn)赶促,那么就一直找尋子節(jié)點(diǎn),直到找到值為止衷笋。找尋過程肯定是由外而內(nèi)芳杏,也就是從根節(jié)點(diǎn)開始,一直往樹狀結(jié)構(gòu)的底部查詢
// 得到根節(jié)點(diǎn)
GDataXMLElement *rootElement = [xmlDocument rootElement];
NSLog(@"根節(jié)點(diǎn)的標(biāo)簽----%@",rootElement.name);
// 在添加一個學(xué)生節(jié)點(diǎn)
// 創(chuàng)造一個student節(jié)點(diǎn)
GDataXMLElement *createStudentNode = [GDataXMLElement elementWithName:@"student"];
// 為student節(jié)點(diǎn)創(chuàng)造子節(jié)點(diǎn)name辟宗,age爵赵,sex
GDataXMLElement *createNameNode = [GDataXMLElement elementWithName:@"name" stringValue:@"大定位"];
GDataXMLElement *createAgeNode = [GDataXMLElement elementWithName:@"age" stringValue:@"12"];
GDataXMLElement *createSexNode = [GDataXMLElement elementWithName:@"sex" stringValue:@"男"];
// 為student節(jié)點(diǎn)添加剛才創(chuàng)建好的子節(jié)點(diǎn)
[createStudentNode addChild:createNameNode];
[createStudentNode addChild:createAgeNode];
[createStudentNode addChild:createSexNode];
// 為根節(jié)點(diǎn)添加剛才創(chuàng)建好的student節(jié)點(diǎn)
[rootElement addChild:createStudentNode];
// 得到根節(jié)點(diǎn)底部的子節(jié)點(diǎn) 由于每個節(jié)點(diǎn)的子節(jié)點(diǎn)都有可能是多個,所以返回值為數(shù)組泊脐。所有子節(jié)點(diǎn)的獲得空幻,都是他們的父節(jié)點(diǎn)通過節(jié)點(diǎn)名稱(標(biāo)簽名稱)得到所有的子節(jié)點(diǎn)。
NSArray *subElement = [rootElement elementsForName:@"student"];
NSLog(@"%@",subElement);
// 取出student節(jié)點(diǎn)的子節(jié)點(diǎn)
for (GDataXMLElement *element in subElement) {
// 此處的element是某一個student節(jié)點(diǎn)
// 取出name節(jié)點(diǎn) 他是student的子節(jié)點(diǎn)
NSArray *nameElement = [element elementsForName:@"name"];
// 由于name節(jié)點(diǎn)沒有子節(jié)點(diǎn)容客,節(jié)點(diǎn)中的值是我們所需要的秕铛。并且我們知道nameElement數(shù)組中只有一個元素。并且他的類型是GDataElement缩挑。但是我們知道該節(jié)點(diǎn)中的值的類型為字符串但两,所以下面需要強(qiáng)制轉(zhuǎn)換。
// 取值第一步:取出上面數(shù)組中的元素
GDataXMLElement *nameValue = [nameElement objectAtIndex:0];
// 第二步:強(qiáng)制轉(zhuǎn)換
NSString *name = [nameValue stringValue];
NSLog(@"nameValue-----%@",name);
}
}
最后在viewDidLoad中調(diào)用方法即可供置。
- (void)viewDidLoad {
[super viewDidLoad];
// [self xmlDomParser];
[self xmlSaxParser];
}