解析JSON数据,解析XML


前言
  • 本文是解析 XML(用XML语言写的数据) 和 JSON格式的数据
  • 在此声明:XML是一种语言,JSON是一种数据格式


    30-01.png

一.解析JSON格式的数据(即:反序列化)


利用NSJSONSerialization对JSON格式的数据进行解析
当数据结构为 {key:value,key:value,...}的键值对的结构时,可以解析成NSDictionary.
当数据结构为 ["a","b","c",...]结构时,可以解析成NSArray.

NSJSONSerialization类中的两个方法
  • JSON数据转成OC对象(反序列化)--->解析JSON格式的数据就用这个方法
  • 反序列化目的(通俗理解):转换成OC格式,对于学习OC语法的人来说,能容易看懂
+ (id)JSONObjectWithData:(NSData *)data options:(NSJSONReadingOptions)opt error:(NSError **)error;
  • OC对象转成JSON数据(序列化)
+ (NSData *)dataWithJSONObject:(id)obj options:(NSJSONWritingOptions)opt error:(NSError **)error;


序列化与反序列化:
  • 序列化:在传输之前,将对象转换成有序的字符串或者二进制数据流
  • 反序列化:将已经接收到的字符串或者二进制数据流 转换成对象或者数组,以便程序访问

注意:
  • Json本质上是具有特殊格式的字符串
  • 解析Json的过程就是反序列化的过程
  • 在线代码格式化:http://tool.oschina.net/codeformat/json
  • 区分字典和数组
  • 字典{:,:,}就是key:vaule的形式,既有冒号又有逗号
  • 数组[,,,,]中都是逗号隔开

JSON数据转成OC对象的详情代码1:
  • 注意:创建task使用的是dataTaskWithRequest方法
30-04.png

JSON数据转成OC对象的详情代码2:
  • 注意:创建task使用的是dataTaskWithURL方法
30-05.png

OC对象转成JSON数据的详情代码:
30-03.png

JSON解析综合练习(用NSJSONSerialization创建JSON解析器)
  • 注意区分里面的面向字典开发和面向模型
#import "ViewController.h"
#import "UIImageView+WebCache.h"
#import "ZBTableViewCell.h"
#import "ZBVideo.h"
#import <MediaPlayer/MediaPlayer.h>
#import "MJExtension.h"

#define BaseURL @"http://120.25.226.186:32812"

@interface ViewController ()
/** 数据源*/
@property (nonatomic ,strong) NSArray *videos;
@end

@implementation ViewController

#pragma mark --------------------
#pragma mark Life Cycle
- (void)viewDidLoad
{
    [super viewDidLoad];
   //有了这句代码,模型中的ID本质对应着的是数据库中的id
    //0.告诉框架新值和旧值的对应关系
    [ZBVideo mj_setupReplacedKeyFromPropertyName:^NSDictionary *{
        return @{
                 @"ID":@"id",
                 };
        
    }];
    //1.发送请求
    [[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@"http://120.25.226.186:32812/video?type=JSON"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        
        //2.解析数据 (NSJSONSerialization是解析数据的标志)
        NSDictionary *dictM  = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
        NSLog(@"%@",dictM);
        
        //复杂json
        //1)写plist文件到桌面
        [dictM writeToFile:@"/Users/zhangbin/Desktop/video.plist" atomically:YES];
        //2)json在线格式化 http://tool.oschina.net/codeformat/json
        
        //3.设置tableView的数据源(把服务器返回给我们的dictM进行转换,作为tableView的数据源,显示在模拟器上)
        
        /*
         **************************************面向字典开发**************************************
        //a.面向字典开发
        //self.videos = dictM[@"videos"];
         
         **************************************面向模型开发**************************************
        //b.面向模型开发
        NSArray *arrayM = dictM[@"videos"];
        //字典数组---->模型数据
        NSMutableArray *arr = [NSMutableArray array];
        for (NSDictionary *dict  in arrayM) {
            [arr addObject:[ZBVideo videWithDict:dict]];
        }
        self.videos = arr;
         
         */
        //************************************面向模型开发**************************************
        //c.利用MJ的框架,进行面向模型开发(只需要在模型类中声明对应的属性,不需要声明和实现接口方法,并且之前在外界字典转模型需要很多行代码,这里只需一行代码就搞定)
        //dictM[@"videos"]
        self.videos = [ZBVideo mj_objectArrayWithKeyValuesArray:dictM[@"videos"]];
        
        //4.刷新tableView
        dispatch_async(dispatch_get_main_queue(), ^{
             [self.tableView reloadData];//当前类必须继承UITableViewController,并且在故事板中设置关联的类就是当前的控制器。否则这行代码打不出来,会提示错误
        });
       
        
    }] resume];
}

#pragma mark --------------------
#pragma mark UITableViewDataSource
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.videos.count;
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *ID = @"video";
    //1.创建cell
    ZBTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    
    
    /*
     **************************************面向字典开发**************************************
     面向字典开发,设置数据的代码(取出字典中的key中才能拿到数据,例如dictM[@"length"])
     
    //NSDictionary *dictM = self.videos[indexPath.row];
    //2.2 设置标题
    cell.textLabel.text = dictM[@"name"];
    
    //2.3 设置子标题
    cell.detailTextLabel.text = [NSString stringWithFormat:@"播放时间:%@ 秒",dictM[@"length"]];
    
    //2.4 设置图标
    NSURL *url = [NSURL URLWithString:[BaseURL stringByAppendingPathComponent:dictM[@"image"]]];
    
    [cell.imageView sd_setImageWithURL:url placeholderImage:[UIImage imageNamed:@"Snip20200808_28"]];

     */
    
    
    //**************************************面向模型开发**************************************
    //面向模型开发,设置数据的代码(直接从模型中访问属性即可拿到数据,例如ideoM.length)
    //2.1 获得该cell对应的数据
    ZBVideo *videoM = self.videos[indexPath.row];
    
    //2.2 设置标题
    cell.textLabel.text = videoM.name;
    
    //2.3 设置子标题
    cell.detailTextLabel.text = [NSString stringWithFormat:@"播放时间:%@ 秒",videoM.length];
    
    //2.4 设置图标 image是NSString类型的
    NSURL *url = [NSURL URLWithString:[BaseURL stringByAppendingPathComponent:videoM.image]];
    
    //占位图片,当网上的图片没下载当指定的cell的时候,用占位图片暂时顶替
    [cell.imageView sd_setImageWithURL:url placeholderImage:[UIImage imageNamed:@"Snip20200808_28"]];
    
    NSLog(@"%@",videoM.ID);
    
    //3.返回cell
    return cell;
}

#pragma mark --------------------
#pragma mark UITableViewDelegate
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    
    /* **************************************面向字典开发**************************************
     
    //1.拿到该cell对应的数据
    NSDictionary *dictM  = self.videos[indexPath.row];
    
    //播放该cell对应的视频
    NSString *urlStr = [BaseURL stringByAppendingPathComponent:dictM[@"url"]];
    */
    
    
    //**************************************面向模型开发**************************************
    //1.拿到该cell对应的数据
    ZBVideo *videoM  = self.videos[indexPath.row];
    
    //播放该cell对应的视频
    NSString *urlStr = [BaseURL stringByAppendingPathComponent:videoM.url];
    
    //2.创建视频播放控制器
    MPMoviePlayerViewController *vc = [[MPMoviePlayerViewController alloc]initWithContentURL:[NSURL URLWithString:urlStr]];
    
    //3.弹出控制器
    [self presentViewController:vc animated:YES completion:nil];
                        
}
@end

上述代码截图
30-06.png

二.解析XML


解析XML的两种做法
  • NSXMLParser(适合大文件)
  • GDataXMLDocument(不适合大文件)

解析XML的做法一(用NSXMLParser创建XML解析器)

  • 注意:和JSON解析综合练习的区别:
  • 解析数据的代码不一样
  • 让控制器成为NSXMLParser的代理,代理名称为NSXMLParserDelegate
  • 字典转模型在代理方法中执行
#import "ViewController.h"
#import "UIImageView+WebCache.h"
#import "ZBTableViewCell.h"
#import "ZBVideo.h"
#import <MediaPlayer/MediaPlayer.h>
#import "MJExtension.h"

#define BaseURL @"http://120.25.226.186:32812"

@interface ViewController ()<NSXMLParserDelegate>
/** 数据源*/
@property (nonatomic ,strong) NSMutableArray *videos;
@end

@implementation ViewController

#pragma mark --------------------
#pragma mark lazy loading

-(NSMutableArray *)videos
{
    if (_videos == nil) {
        _videos = [NSMutableArray array];
    }
    return _videos;
}
#pragma mark --------------------
#pragma mark Life Cycle
- (void)viewDidLoad
{
    [super viewDidLoad];
   
    //0.告诉框架新值和旧值的对应关系
    [ZBVideo mj_setupReplacedKeyFromPropertyName:^NSDictionary *{
        return @{
                 @"ID":@"id",
                 };
        
    }];
    //1.发送请求
    [[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@"http://120.25.226.186:32812/video?type=XML"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        
        //2.解析数据
        //2.1 创建XML解析器
        NSXMLParser *parser = [[NSXMLParser alloc]initWithData:data];
        
        //2.2 设置代理
        parser.delegate = self;
        
        //2.3 开始解析,该方法本身是阻塞的
        [parser parse];
        
        //4.刷新tableView
        dispatch_async(dispatch_get_main_queue(), ^{
             [self.tableView reloadData];
        });
       
        
    }] resume];
}

#pragma mark --------------------
#pragma mark UITableViewDataSource
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.videos.count;
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *ID = @"video";
    //1.创建cell
    ZBTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    
    //2.设置数据

    //2.1 获得该cell对应的数据
  
    ZBVideo *videoM = self.videos[indexPath.row];
    
    //2.2 设置标题
    cell.textLabel.text = videoM.name;
    
    //2.3 设置子标题
    cell.detailTextLabel.text = [NSString stringWithFormat:@"播放时间:%@ 秒",videoM.length];
    
    //2.4 设置图标
    NSURL *url = [NSURL URLWithString:[BaseURL stringByAppendingPathComponent:videoM.image]];
    
    [cell.imageView sd_setImageWithURL:url placeholderImage:[UIImage imageNamed:@"Snip20200808_28"]];
    
    NSLog(@"%@",videoM.ID);
    
    //3.返回cell
    return cell;
}

#pragma mark --------------------
#pragma mark UITableViewDelegate
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{

    //1.拿到该cell对应的数据
    ZBVideo *videoM  = self.videos[indexPath.row];
    
    //播放该cell对应的视频
    NSString *urlStr = [BaseURL stringByAppendingPathComponent:videoM.url];
    
    //2.创建视频播放控制器
    MPMoviePlayerViewController *vc = [[MPMoviePlayerViewController alloc]initWithContentURL:[NSURL URLWithString:urlStr]];
    
    //3.弹出控制器
    [self presentViewController:vc animated:YES completion:nil];
                        
}

#pragma mark --------------------
#pragma mark NSXMLParserDelegate
//1.开始解析XML文档
-(void)parserDidStartDocument:(NSXMLParser *)parser
{
    NSLog(@"%s",__func__);
}

//2.开始解析某个元素   attributeDict:存放着元素的属性
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary<NSString *,NSString *> *)attributeDict
{
     NSLog(@"%s--开始解析元素%@---\n%@",__func__,elementName,attributeDict);
    
    if ([elementName isEqualToString:@"videos"]) {
        //过滤根元素
        return;
    }
    ZBVideo *videoM = [[ZBVideo alloc]init];
    [videoM mj_setKeyValues:attributeDict];
    [self.videos addObject:videoM];//先调用videos的懒加载方法。再将模型装进大的数组中

}

//3.某个元素解析完毕
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
     NSLog(@"%s--结束%@元素的解析",__func__,elementName);
}

//4.整个XML文档都已经解析完毕
-(void)parserDidEndDocument:(NSXMLParser *)parser
{
    NSLog(@"%s",__func__);
}
@end


利用NSXMLParser解析XML和利用NSJSONSerialization解析JSON数据的区别
30-07.png
30-08.png
30-09.png

解析XML的做法二(用GDataXMLDocument创建XML解析器)
  • 注意:和NSXMLParser的区别:
    • 解析数据的代码不一样
    • 不需要设置代理
#import "ViewController.h"
#import "UIImageView+WebCache.h"
#import "ZBTableViewCell.h"
#import "ZBVideo.h"
#import <MediaPlayer/MediaPlayer.h>
#import "MJExtension.h"
#import "GDataXMLNode.h"

#define BaseURL @"http://120.25.226.186:32812"

@interface ViewController ()<NSXMLParserDelegate>
/** 数据源*/
@property (nonatomic ,strong) NSMutableArray *videos;
@end

@implementation ViewController

#pragma mark --------------------
#pragma mark lazy loading

-(NSMutableArray *)videos
{
    if (_videos == nil) {
        _videos = [NSMutableArray array];
    }
    return _videos;
}
#pragma mark --------------------
#pragma mark Life Cycle
- (void)viewDidLoad
{
    [super viewDidLoad];
   
    //有了这句代码,模型中的ID本质对应着的是数据库中的id
    
    //0.告诉框架新值和旧值的对应关系
    [ZBVideo mj_setupReplacedKeyFromPropertyName:^NSDictionary *{
        return @{
                 @"ID":@"id",
                 };
        
    }];
    //1.发送请求
    [[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@"http://120.25.226.186:32812/video?type=XML"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        
        //2.解析数据
                //2.1 加载整个XML文档
        GDataXMLDocument *doc = [[GDataXMLDocument alloc]initWithData:data options:kNilOptions error:nil];
        
        //2.2 拿到根元素,得到根元素内部所有名称为video的属性
       NSArray *eles =  [doc.rootElement elementsForName:@"video"];
        
        //2.3 遍历所有的子元素,得到元素内部的属性数据
        for (GDataXMLElement *ele in eles) {
            ZBVideo *videoM = [[ZBVideo alloc]init];
            
            videoM.name = [ele attributeForName:@"name"].stringValue;
            videoM.image = [ele attributeForName:@"image"].stringValue;
            videoM.ID = [ele attributeForName:@"id"].stringValue;
            videoM.length = [ele attributeForName:@"length"].stringValue;
            videoM.url = [ele attributeForName:@"url"].stringValue;
            [self.videos addObject:videoM];
        }
        
        //4.刷新tableView
        dispatch_async(dispatch_get_main_queue(), ^{
             [self.tableView reloadData];
        });
       
        
    }] resume];
}

#pragma mark --------------------
#pragma mark UITableViewDataSource
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.videos.count;
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *ID = @"video";
    //1.创建cell
    ZBTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    
    //2.设置数据

    //2.1 获得该cell对应的数据
  
    ZBVideo *videoM = self.videos[indexPath.row];
    
    //2.2 设置标题
    cell.textLabel.text = videoM.name;
    
    //2.3 设置子标题
    cell.detailTextLabel.text = [NSString stringWithFormat:@"播放时间:%@ 秒",videoM.length];
    
    //2.4 设置图标
    NSURL *url = [NSURL URLWithString:[BaseURL stringByAppendingPathComponent:videoM.image]];
    
    [cell.imageView sd_setImageWithURL:url placeholderImage:[UIImage imageNamed:@"Snip20200808_28"]];
    
   
    NSLog(@"%@",videoM.ID);
    
    //3.返回cell
    return cell;
}

#pragma mark --------------------
#pragma mark UITableViewDelegate
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    //1.拿到该cell对应的数据
    ZBVideo *videoM  = self.videos[indexPath.row];
    
    //播放该cell对应的视频
    NSString *urlStr = [BaseURL stringByAppendingPathComponent:videoM.url];
    
    //2.创建视频播放控制器
    MPMoviePlayerViewController *vc = [[MPMoviePlayerViewController alloc]initWithContentURL:[NSURL URLWithString:urlStr]];
    
    //3.弹出控制器
    [self presentViewController:vc animated:YES completion:nil];
                        
}

@end


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容