代码改变世界

iOS开发——XML/JSON数据解析

2016-12-21 21:44  supper_Ho  阅读(414)  评论(0编辑  收藏  举报

 

 

 

 

NSJSONSerialization

 

 

接下来就正式开始。苹果官方给出的解析方式是性能最优越的,虽然用起来稍显复杂。

首先我们在上面已经有了我希望得到的信息的网站的API给我们的URL,在OC中,我要加载一个NSURL对象,来向网站提交一个Request。到这里需要特别注意了,iOS9的时代已经来临,我们先前在旧版本中使用的某些类或者方法都已经被苹果官方弃用了。刚刚我们向网站提交了一个Request,在以往,我们是通过NSURLConnection中的sendSynchronousRequest方法来接受网站返回的Response的,但是在iOS9中,它已经不再使用了。从官方文档中,我们追根溯源,找到了它的替代品——NSURLSession类。这个类是iOS7中新的网络接口,苹果力推之,并且现在用它完全替代了NSURLConnection。关于它的具体用法,还是蛮简单的,直接上代码(ViewController.m文件):



文/神兽gcc(简书作者)
原文链接:http://www.jianshu.com/p/a54d367adb2a
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
 
#import "ViewController.h"

@interface ViewController ()
@property (retain, nonatomic) IBOutlet UITextView *textView;
@property (nonatomic, strong) NSMutableDictionary *dic;
@property (nonatomic,strong) NSString *text;
@end

@implementation ViewController
- (IBAction)NSJson:(UIButton *)sender {
    //GCD异步实现
    dispatch_queue_t q1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(q1, ^{

        //加载一个NSURL对象
        NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://api.douban.com/v2/movie/subject/25881786"]];

        //使用NSURLSession获取网络返回的Json并处理
        NSURLSession *session = [NSURLSession sharedSession];
        NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error){

            //从网络返回了Json数据,我们调用NSJSONSerialization解析它,将JSON数据转换为Foundation对象(这里是一个字典)
            self.dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];

            NSString *title = [self.dic objectForKey:@"original_title"];
            NSMutableArray *genresArray = [self.dic objectForKey:@"genres"];
            NSString *genres = [NSString stringWithFormat:@"%@/%@", [genresArray objectAtIndex:0], [genresArray objectAtIndex:1]];
            NSString *summary = [self.dic objectForKey:@"summary"];

            self.text = [NSString stringWithFormat:@"电影名称:\n%@\n体裁:\n%@\n剧情简介:\n%@", title, genres, summary];

            //更新UI操作需要在主线程
            dispatch_async(dispatch_get_main_queue(), ^{
                self.textView.text = self.text;
            });
        }];
        //调用任务
        [task resume];
    });
}
 
 

NSXMLParse

 

 

关于XML,有两种解析方式,分别是SAX(Simple API for XML,基于事件驱动的解析方式,逐行解析数据,采用协议回调机制)和DOM(Document Object Model ,文档对象模型。解析时需要将XML文件整体读入,并且将XML结构化成树状,使用时再通过树状结构读取相关数据,查找特定节点,然后对节点进行读或写)。苹果官方原生的NSXMLParse类库采用第一种方式,即SAX方式解析XML,它基于事件通知的模式,一边读取文档一边解析数据,不用等待文档全部读入以后再解析,所以如果你正打印解析的数据,而解析过程中间出现了错误,那么在错误节点之间的数据会正常打印,错误后面的数据不会被打印。解析过程由NSXMLParserDelegate协议方法回调。

文/神兽gcc(简书作者)
原文链接:http://www.jianshu.com/p/a54d367adb2a
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

我们遵循MVC,首先我们创建模型,新建一个person类,存放XML文件中描述的person属性。再来一个解析XML文件的工具类XMLUtil,我们在里面实现文件的获取,代理方法的实现。

先来看这两个类的代码:

 

//person.h

#import <Foundation/Foundation.h>

@interface person : NSObject
@property (nonatomic, copy) NSString *pid;
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *sex;
@property (nonatomic, copy) NSString *age;
@end



//XMLUtil.h

#import <Foundation/Foundation.h>
#import "person.h"
//声明代理
@interface XMLUtil : NSObject<NSXMLParserDelegate>
//添加属性
@property (nonatomic, strong) NSXMLParser *par;
@property (nonatomic, strong) person *person;
//存放每个person
@property (nonatomic, strong) NSMutableArray *list;
//标记当前标签,以索引找到XML文件内容
@property (nonatomic, copy) NSString *currentElement;

//声明parse方法,通过它实现解析
-(void)parse;
@end



//XMLUtil.m

#import "XMLUtil.h"

@implementation XMLUtil

- (instancetype)init{
    self = [super init];
    if (self) {
        //获取事先准备好的XML文件
        NSBundle *b = [NSBundle mainBundle];
        NSString *path = [b pathForResource:@"test" ofType:@".xml"];
        NSData *data = [NSData dataWithContentsOfFile:path];
        self.par = [[NSXMLParser alloc]initWithData:data];
        //添加代理
        self.par.delegate = self;
        //初始化数组,存放解析后的数据
        self.list = [NSMutableArray arrayWithCapacity:5];
    }
    return self;
}

//几个代理方法的实现,是按逻辑上的顺序排列的,但实际调用过程中中间三个可能因为循环等问题乱掉顺序
//开始解析
- (void)parserDidStartDocument:(NSXMLParser *)parser{
    NSLog(@"parserDidStartDocument...");
}
//准备节点
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(nullable NSString *)namespaceURI qualifiedName:(nullable NSString *)qName attributes:(NSDictionary<NSString *, NSString *> *)attributeDict{

    self.currentElement = elementName;

    if ([self.currentElement isEqualToString:@"student"]){
        self.person = [[person alloc]init];

    }

}
//获取节点内容
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{

    if ([self.currentElement isEqualToString:@"pid"]) {

        [self.person setPid:string];
    }else if ([self.currentElement isEqualToString:@"name"]){
        [self.person setName:string];
    }else if ([self.currentElement isEqualToString:@"sex"]){
        [self.person setSex:string];
    }else if ([self.currentElement isEqualToString:@"age"]){

        [self.person setAge:string];
    }
}

//解析完一个节点
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(nullable NSString *)namespaceURI qualifiedName:(nullable NSString *)qName{

    if ([elementName isEqualToString:@"student"]) {
        [self.list addObject:self.person];
    }
    self.currentElement = nil;
}

//解析结束
- (void)parserDidEndDocument:(NSXMLParser *)parser{
    NSLog(@"parserDidEndDocument...");
}

//外部调用接口
-(void)parse{
    [self.par parse];

}

@end


OK,总算是大功告成,如果对代理的使用比较熟悉的话,这部分内容其实还蛮简单的。如果被代码转来转去弄晕了的话可以在每个block的最后都加一个打印输出,做好标记,你就能弄懂程序的执行顺序了。

我们点击NSXMLParse,有了!