iOS 網絡數據之XML解析 – iPhone手機開發技術文章 iPhone軟體開發教學課程



向服務器請求數據,那麼數據必須以某個特定的格式存放,然後一方把數據按這種格式組織起來,另一方按相同的方式把數據解析數來,就像是我們人之間講話交流,我們的話會轉變成振動、在空氣中傳播、然後對方的耳朵感受這種振動,然後把振動轉化為話,所以我認為格式的組織是為瞭更好的傳遞數據。一般網絡數據會封裝成兩種格式進行傳遞: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數據選擇使用百度天氣接口的數據,地址:https://api.map.baidu.com/telematics/v3/weather?location=%E5%8C%97%E4%BA%AC&ak=5slgyqGDENN7Sy7pw29IUvrZ 。關於天氣接口的配置,可以參考:百度天氣接口

(1)獲取數據:

-(void )getXMLData{
    NSString * URLStr = @"https://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格式。

發佈留言