解析Json 和 XML 的学习


1.  开题

        Json
        XML

  • Json格式:
    • NSJSONSerialization,官方提供的Json数据格式解析类,iOS5以后支持
    • JSONKit(第三方类库)
    • SBJson
    • TouchJson

    • TouchJson包下载: http://download.csdn.net/detail/enuola/4523169
    • SBJson 包下载: http://download.csdn.net/detail/enuola/4523177
      JSONKit包下载:https://github.com/TouchCode/TouchJSON

  • XML格式:
    • NSXMLParse,官方自带
    • GDataXML,Google提供的开元XML解析;
      (1)NSJSONSerialization

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

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

      参数:

      NSJSONReadingMutableContainers:返回可变容器,NSMutableDictionary或NSMutableArray。(注意:返回的必须是字典或数组); 
       
      NSJSONReadingMutableLeaves:返回的JSON对象中字符串的值为NSMutableString,目前在iOS 7上测试不好用,应该是个bug,参见: 
      http://stackoverflow.com/questions/19345864/nsjsonreadingmutableleaves-option-is-not-working 
       
      NSJSONReadingAllowFragments:允许JSON字符串最外层既不是NSArray也不是NSDictionary,但必须是有效的JSON Fragment。例如使用这个选项可以解析 @“123” 这样的字符串。参见: 
      http://stackoverflow.com/questions/16961025/nsjsonserialization-nsjsonreadingallowfragments-reading 

       



#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];     ///  记住要调用否则方法不会被调用
    });
}
JsonKit

事实上,它虽然不支持ARC,但JsonKit是在性能上仅次于苹果原生解析器的第三方类库。我们在导入它的包以后编译会出现一大堆报错,这时候不用慌,我们会发现大部分是ARC的问题,解决方法也挺简单,我们进入项目的Target,找到Build Phases里面的Compile Sources,接着找我们的问题源头JsonKit.m,双击更改它的Compiler Flags标签为“-fno-objc-arc”,再次编译,就好啦~

//上面先导入包:
#import "ViewController.h"
#import "JsonKit.h"

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

        //还是先获取url
        NSURL *url = [NSURL URLWithString:@"https://api.douban.com/v2/movie/subject/26279433"];
        NSString *jsonString = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];
        //代码越来越简单了有木有!!就一个方法搞定~
        self.dic = [jsonString objectFromJSONStringWithParseOptions:JKParseOptionLooseUnicode];


        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;
        });
    });
}

虽然我们只用了一个方法,但是这可不代表JsonKit类库里就只有这一个解析的方法,我们可以去看看它的源码来找寻一番。一般来讲,如果json是“单层”的,即value都是字符串、数字,可以使用objectFromJSONString方法,这个也比较简单。如果json有嵌套,即value里有array、object,如果再使用objectFromJSONString,程序可能会报错,这时我们最好使用objectFromJSONStringWithParseOptions也就是我代码里使用的这个方法,因为电影体裁的Value是数组类型的。



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

TouchJson

来看看最后一个:

//导入包:
#import "ViewController.h"
#import "CJSONSerializer.h"
#import "CJSONDeserializer.h"


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

        //还是先获取url
        NSURL *url = [NSURL URLWithString:@"https://api.douban.com/v2/movie/subject/22265299"];
        NSString *jsonString = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];
        //还是一句话的事儿
        self.dic = [[CJSONDeserializer deserializer] deserialize:[jsonString dataUsingEncoding:NSUTF8StringEncoding] 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;
        });
    });
}


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

NSXMLParse
//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


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

来看GDataXML,它是一种DOM方式的解析类库。DOM实现的原理是把整个xml文档一次性读出,放在一个树型结构里。在需要的时候,查找特定节点,然后对节点进行读或写。

在使用之前呢,我们还是先从网上下载GDataXML包,里面两个文件GDataXMLNode.h和GDataXMLNode.m导入到项目中来,编译,发现报错了,这是因为GDataXML是依赖libmxl2的,我们要去项目的Target中做一些设置。

  • 找到项目的Tarfet,进入Build Phases里面的Link Binary With Libraries,点击“加号”,搜索libxml,把出现的包添加进去,这里最新版的XCode7和iOS9中,是libxml.2.2.tbd。
  • 再来到Build Settings,我们可以搜索一下,找到Header Search Paths,添加路径“/usr/include/libxml2”。
  • 再找到Other Link Flags,添加“-libxml2“
  • 还有就是如果你下载的GDataXML是不支持ARC的,那么你就要像上面那样去添加“-fno-objc-arc”,这个视你下载的GDataXML包版本而定。

再次编译,就顺利通过了。

接下来看看我们怎么用这个东西。贴代码之前我真的想说一句,比起苹果原生的类库,这些开源的第三方类库真的在用起来的时候不知道有多舒服,懒人必备啊。在实际的开发中可以为我们节省很多的时间与精力,但是还是要搞懂人家原生的东西,这样才叫学会了么。

//ViewController.m

- (IBAction)GDataXML:(id)sender {

    NSString *path = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"xml"];
    NSData *data = [[NSData alloc]initWithContentsOfFile:path];
    //对象初始化
    GDataXMLDocument *doc = [[GDataXMLDocument alloc]initWithData:data error:nil];
    //获取根节点
    GDataXMLElement *rootElement = [doc rootElement];
    //获取其他节点
    NSArray *students = [rootElement elementsForName:@"student"];
    //初始化可变数组,用来显示到textView
    self.GDatatext = [[NSMutableString alloc]initWithString:@""];
    for (GDataXMLElement *student in students) {
        //获取节点属性
        GDataXMLElement *pidElement = [[student elementsForName:@"pid"] objectAtIndex:0];
        NSString *pid = [pidElement stringValue];


        GDataXMLElement *nameElement = [[student elementsForName:@"name"] objectAtIndex:0];
        NSString *name = [nameElement stringValue];


        GDataXMLElement *sexElement = [[student elementsForName:@"sex"] objectAtIndex:0];
        NSString *sex = [sexElement stringValue];


        GDataXMLElement *ageElement = [[student elementsForName:@"age"] objectAtIndex:0];
        NSString *age = [ageElement stringValue];

        //调整一下姿势,添加到可变长字符串~~
        NSString *t = [NSString stringWithFormat:@"学号:%@ 姓名:%@ 性别:%@ 年龄:%@\n", pid, name, sex, age];
        [self.GDatatext appendString:t];
    }
    self.textView.text = self.GDatatext;
}


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





  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值