iOS 網路資料之XML解析
向伺服器請求資料,那麼資料必須以某個特定的格式存放,然後一方把資料按這種格式組織起來,另一方按相同的方式把資料解析數來,就像是我們人之間講話交流,我們的話會轉變成震動、在空氣中傳播、然後對方的耳朵感受這種震動,然後把震動轉化為話,所以我認為格式的組織是為了更好的傳遞資料。一般網路資料會封裝成兩種格式進行傳遞:XML和json。
1、”解析“:
XML長得和HTML很像,開啟瀏覽器的顯示源碼功能,可以看到一串串的<>標籤,而解析XML也依靠於這些標籤。首先說下我理解的“解析”,不管是XML還是json,實際就是一大段字串,解析就是按XML或json的格式規範把這個字串變成字典或數組,這樣才能自由的擷取裡面的資料。比如:
晴轉多雲微風34 ~ 23℃
weather標籤裡面是”晴轉多雲“,就是說天氣是晴轉多雲,那麼這就是字典裡面的一個鍵值對,weather是鍵,晴轉多雲是值。XML、json和數組字典都是組織資料的東西,“解析”就是把資料從某種組織格式轉為另一種。 2、DOM和SAX:
這是XML的兩種解析方式,DOM是把XML文檔整個的載入,對於這個文檔裡資料的結構都清楚了;然後可以使用XPath直接擷取某個節點;而SAX是從XML頭部逐條逐個標籤的向下讀,遇到一個什麼東西就通知一下,所以它不需要XML文檔已經全部擷取,但是它只管得了當前讀到的地方,前面讀過的就取不到了,所以在結構上不是很清楚,但是相對快速、耗記憶體小。個人覺得DOM解析操作起來更方便一些。
3、關於解析類庫的選擇:
參看文章:iOS平台XML解析類庫對比概述
4、系統的NSXMLParser的使用:
XML資料選擇使用百度天氣介面的資料,地址:http://api.map.baidu.com/telematics/v3/weather?location=%E5%8C%97%E4%BA%AC&ak=5slgyqGDENN7Sy7pw29IUvrZ 。關於天氣介面的配置,可以參考:百度天氣介面
(1)擷取資料:
-(void )getXMLData{ NSString * URLStr = @"http://api.map.baidu.com/telematics/v3/weather?location=%E5%8C%97%E4%BA%AC&ak=5slgyqGDENN7Sy7pw29IUvrZ"; NSURL * url = [NSURL URLWithString:URLStr]; NSURLRequest * request = [[NSURLRequest alloc]initWithURL:url]; _XMLData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil]; } (2)構建NSXMLParser:
-(void )parserXMLData{ _parser = [[NSXMLParser alloc]initWithData:_XMLData]; _parser.delegate = self; if (![_parser parse]) { NSLog(@"Parser start error"); }}這裡是使用NSData對象構建,也可以使用文檔資源地址構建- (id)initWithData:(NSData *)data、使用輸入資料流構建:- (id)initWithStream:(NSInputStream *)stream。調用parse開啟解析。需要設定委派物件,這樣在解析到某個元素變遷或是字元的時候,會讓委派物件調用委託方法,而就是在這些方法裡面進行資料的處理。
(3)委託方法:-(void )parserDidStartDocument:(NSXMLParser *)parser{ //開始解析文檔是調用 NSLog(@"doucment start");}-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{ //每次解析到一個標籤的時候調用,例如上面晴轉多雲中的標籤 _currentElement = elementName; //把當前的標籤儲存下來,便於下面方法裡判斷 if ([_currentElement isEqualToString:@"weather_data"]) { //當開始解析weather_data這個標籤的時候構建數組,用於儲存多天的天氣資訊。 _weatherArray = [[NSMutableArray alloc]init]; }else if ([_currentElement isEqualToString:@"date"]&&_weatherArray){//因為date標籤是每一天天氣資訊的開始變遷,所以在解析到date標籤的時候構建一個字典,用來儲存某一天的天氣資訊,並且加入到_weatherArray裡面 NSMutableDictionary * oneDayWeather = [[NSMutableDictionary alloc]init]; [_weatherArray addObject:oneDayWeather]; }}-(void )parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{ //每次解析到一個字串,在XML文檔裡,除了標籤,就是資料,而資料有些數字串,所以字串資料時會調用這個方法,例如上面的晴轉多雲中的晴轉多雲。 string = [string stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@" \n\r"]]; //通過這個方法去掉空格、換行字串,保證把有用的字串存進來 if (_weatherArray&&string.length>0) { if ([_currentElement isEqualToString:@"date"]) { //使用_currentElement的值來判斷當前是處於哪一個標籤內部 NSMutableDictionary * oneDayWeather = _weatherArray.lastObject; //因為SAX解析從上到下,所以lastObject就是最新的一個字典,也就是當前需要存入資料的字典 [oneDayWeather setObject:string forKey:@"date"]; }else if ([_currentElement isEqualToString:@"dayPictureUrl"]){ NSMutableDictionary * oneDayWeather = _weatherArray.lastObject; [oneDayWeather setObject:string forKey:@"dayPictureUrl"]; }else if ([_currentElement isEqualToString:@"nightPictureUrl"]){ NSMutableDictionary * oneDayWeather = _weatherArray.lastObject; [oneDayWeather setObject:string forKey:@"nightPictureUrl"]; }else if ([_currentElement isEqualToString:@"weather"]){ NSMutableDictionary * oneDayWeather = _weatherArray.lastObject; [oneDayWeather setObject:string forKey:@"weather"]; }else if ([_currentElement isEqualToString:@"wind"]){ NSMutableDictionary * oneDayWeather = _weatherArray.lastObject; [oneDayWeather setObject:string forKey:@"wind"]; }else if ([_currentElement isEqualToString:@"temperature"]){ NSMutableDictionary * oneDayWeather = _weatherArray.lastObject; [oneDayWeather setObject:string forKey:@"temperature"]; } }}-(void )parser:(NSXMLParser *)parser foundCDATA:(NSData *)CDATABlock{ //和上面的字串類似,有些資料不是字串,而是位元組,這裡就是NSData對象 NSLog(@"%@",[[NSString alloc]initWithData:CDATABlock encoding:NSUTF8StringEncoding]);}-(void )parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{ // 當解析到一個標籤的結束的時候調用,每一個標籤都是開頭、結尾兩個成對存在的,例如晴轉多雲中的和,當解析到的時候就是一個標籤的結束,也就會調用這個方法。 //NSLog(@" elementName : %@ , namespaceURI : %@ , qualifiedName : %@ ",elementName,namespaceURI,qName);}-(void )parserDidEndDocument:(NSXMLParser *)parser{ //這是文檔整個解析完畢的時候調用 NSLog(@"document end"); NSLog(@"%@",_weatherArray);}上面列舉了一些解析過程中調用的委託方法,還有許多其他方法,參考文檔中NSXMLParserDelegate。似乎方法很詳細,把解析過程的每個細節的考慮到了,但是感覺把XML中的資料合理的存放到數組字典裡,真不是件容易的事,不想json,一句話就搞定了。SAX解析最大的不便是,當解析到某個標籤的時候,你不知道它上一層的標籤是什麼,更不知道上上層的標籤是什麼,也就是說沒有東西能告訴你整個文檔的結構是什麼樣的。上面的例子是將連結中獲得的天氣XML文檔中天氣的部分取出來放進數組_weatherArray裡面,也就是...這個標籤內的內容。 SAX解析總的來說,不是很方便,如果資料的階層比較複雜,處理起來會很麻煩,所以要考慮DOM解析和json格式。