首页 > 代码库 > iOS 网络数据之XML解析

iOS 网络数据之XML解析

<pre name="code" class="objc"><span style="font-family:Arial, Helvetica, sans-serif;"><span style="font-size:14px;"><span style="background-color: rgb(255, 255, 255);"></span></span></span><address><span style="font-size:14px;"></span></address>

向服务器请求数据,那么数据必须以某个特定的格式存放,然后一方把数据按这种格式组织起来,另一方按相同的方式把数据解析数来,就像是我们人之间讲话交流,我们的话会转变成振动、在空气中传播、然后对方的耳朵感受这种振动,然后把振动转化为话,所以我认为格式的组织是为了更好的传递数据。一般网络数据会封装成两种格式进行传递:XMLjson

   1、”解析“:

   XML长得和HTML很像,打开浏览器的显示源码功能,可以看到一串串的<>标签,而解析XML也依靠于这些标签。首先说下我理解的“解析”,不管是XML还是json,实际就是一大段字符串,解析就是按XML或json的格式规范把这个字符串变成字典或数组,这样才能自由的获取里面的数据。比如:

<span style="font-size:14px;"><weather>晴转多云</weather>
<wind>微风</wind>
<temperature>34 ~ 23℃</temperature></span>
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)获取数据:

<span style="font-size:14px;">-(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 = http://www.mamicode.com/[NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];>

   (2)构建NSXMLParser:

<span style="font-size:14px;">-(void )parserXMLData{
    _parser = [[NSXMLParser alloc]initWithData:_XMLData];
    _parser.delegate = self;
    if (![_parser parse]) {
        NSLog(@"Parser start error");
    }
}
</span>
这里是使用NSData对象构建,也可以使用文档资源地址构建- (id)initWithData:(NSData *)data、使用输入流构建:- (id)initWithStream:(NSInputStream *)stream。调用parse开启解析。需要设置委托对象,这样在解析到某个元素变迁或是字符的时候,会让委托对象调用委托方法,而就是在这些方法里面进行数据的处理。

    (3)委托方法:
<span style="font-size:14px;">-(void )parserDidStartDocument:(NSXMLParser *)parser{  //开始解析文档是调用
    NSLog(@"doucment start");
}

-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{  //每次解析到一个标签的时候调用,例如上面<span style="font-family: Arial, Helvetica, sans-serif;"><weather>晴转多云</weather>中的</span><span style="font-family: Arial, Helvetica, sans-serif;"><weather>标签</span>
    _currentElement = elementName; //把当前的标签保存下来,便于下面方法里判断
    if ([_currentElement isEqualToString:@"weather_data"]) { //当开始解析<span style="font-family: Arial, Helvetica, sans-serif;">weather_data这个标签的时候构建数组,用于保存多天的天气信息。</span>
        _weatherArray = [[NSMutableArray alloc]init];
    }else if ([_currentElement isEqualToString:@"date"]&&_weatherArray){//因为date标签是每一天天气信息的开始变迁,所以在解析到date标签的时候构建一个字典,用来保存某一天的天气信息,并且加入到<span style="font-family: Arial, Helvetica, sans-serif;">_weatherArray里面</span>
        NSMutableDictionary * oneDayWeather = [[NSMutableDictionary alloc]init];
        [_weatherArray addObject:oneDayWeather];
    }
}

-(void )parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{ //每次解析到一个字符串,在XML文档里,除了标签,就是数据,而数据有些数字符串,所以字符串数据时会调用这个方法,例如上面的<span style="font-family: Arial, Helvetica, sans-serif;"><weather>晴转多云</weather>中的晴转多云。</span>
    string = [string stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@" \n\r"]]; //通过这个方法去掉空格、换行字符串,保证把有用的字符串存进来
    if (_weatherArray&&string.length>0) {
        if ([_currentElement isEqualToString:@"date"]) { //使用<span style="font-family: Arial, Helvetica, sans-serif;">_currentElement的值来判断当前是处于哪一个标签内部</span>
            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{ // 当解析到一个标签的结束的时候调用,每一个标签都是开头、结尾两个成对存在的,例如<span style="font-family: Arial, Helvetica, sans-serif;"><weather>晴转多云</weather>中的<weather>和</weather>,当解析到</weather>的时候就是一个标签的结束,也就会调用这个方法。</span>
    //NSLog(@" elementName : %@ , namespaceURI : %@ , qualifiedName : %@ ",elementName,namespaceURI,qName);

}

-(void )parserDidEndDocument:(NSXMLParser *)parser{  //这是文档整个解析完毕的时候调用
    NSLog(@"document end");
    
    NSLog(@"%@",_weatherArray);
}
</span>
上面列举了一些解析过程中调用的委托方法,还有许多其他方法,参考文档中NSXMLParserDelegate。似乎方法很详细,把解析过程的每个细节的考虑到了,但是感觉把XML中的数据合理的存放到数组字典里,真不是件容易的事,不想json,一句话就搞定了。SAX解析最大的不便是,当解析到某个标签的时候,你不知道它上一层的标签是什么,更不知道上上层的标签是什么,也就是说没有东西能告诉你整个文档的结构是什么样的。上面的例子是将链接中获得的天气XML文档中天气的部分取出来放进数组_weatherArray里面,也就是<weather_data>...</weather_data>这个标签内的内容

    SAX解析总的来说,不是很方便,如果数据的层次结构比较复杂,处理起来会很麻烦,所以要考虑DOM解析和json格式。