基本JSON, XML解析,和大文件下载压缩

1 JSON简单介绍
1) 什么是JSON
(1)JSON是一种轻量级的数据格式,一般用于数据交互
(2)服务器返回给客户端的数据,一般都是JSON格式或者XML格式(文件下载除外)
2) 相关说明
(1)JSON的格式很像OC中的字典和数组
(2)标准JSON格式key必须是双引号
3)JSON解析方案
a.第三方框架 JSONKit\SBJSON\TouchJSON
b.苹果原生(NSJSONSerialization)

2 JSON解析相关代码
1)json数据->OC对象
//把json数据转换为OC对象
-(void)jsonToOC
{
//1. 确定url路径
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=33&pwd=33&type=JSON"];

        //2.创建一个请求对象
        NSURLRequest *request = [NSURLRequest requestWithURL:url];

        //3.使用NSURLSession发送一个异步请求
        [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {

            //4.当接收到服务器响应的数据后,解析数据(JSON--->OC)

            /*
             第一个参数:要解析的JSON数据,是NSData类型也就是二进制数据
             第二个参数: 解析JSON的可选配置参数
             NSJSONReadingMutableContainers 解析出来的字典和数组是可变的
             NSJSONReadingMutableLeaves 解析出来的对象中的字符串是可变的  iOS7以后有问题
             NSJSONReadingAllowFragments 被解析的JSON数据如果既不是字典也不是数组, 那么就必须使用这个
             */
            NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
            NSLog(@"%@",dict);

        }];
    }

2)OC对象->JSON对象
     //1.要转换成JSON数据的OC对象*这里是一个字典
        NSDictionary *dictM = @{
                                @"name":@"wendingding",
                                @"age":@100,
                                @"height":@1.72
                                };
        //2.OC->JSON
        /*
         注意:可以通过+ (BOOL)isValidJSONObject:(id)obj;方法判断当前OC对象能否转换为JSON数据
         具体限制:
             1.obj 是NSArray 或 NSDictionay 以及他们派生出来的子类
             2.obj 包含的所有对象是NSString,NSNumber,NSArray,NSDictionary 或NSNull
             3.字典中所有的key必须是NSString类型的
             4.NSNumber的对象不能是NaN或无穷大
         */
        /*
         第一个参数:要转换成JSON数据的OC对象,这里为一个字典
         第二个参数:NSJSONWritingPrettyPrinted对转换之后的JSON对象进行排版,无意义
         */
        NSData *data = [NSJSONSerialization dataWithJSONObject:dictM options:NSJSONWritingPrettyPrinted error:nil];

        //3.打印查看Data是否有值
        /*
         第一个参数:要转换为STring的二进制数据
         第二个参数:编码方式,通常采用NSUTF8StringEncoding
         */
        NSString *strM = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
        NSLog(@"%@",strM);

3)OC对象和JSON数据格式之间的一一对应关系
    //OC对象和JSON数据之间的一一对应关系
    -(void)oCWithJSON
    {
        //JSON的各种数据格式
        //NSString *test = @"\"wendingding\"";
        //NSString *test = @"true";
        NSString *test = @"{\"name\":\"wendingding\"}";

        //把JSON数据->OC对象,以便查看他们之间的一一对应关系
        //注意点:如何被解析的JSON数据如果既不是字典也不是数组(比如是NSString), 那么就必须使用这NSJSONReadingAllowFragments
        id obj = [NSJSONSerialization JSONObjectWithData:[test dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:nil];

        NSLog(@"%@", [obj class]);


        /* JSON数据格式和OC对象的一一对应关系
             {} -> 字典
             [] -> 数组
             "" -> 字符串
             10/10.1 -> NSNumber
             true/false -> NSNumber
             null -> NSNull
         */
    }
    }
4)如何查看复杂的JSON数据
方法一:
    在线格式化http://tool.oschina.net/codeformat/json
方法二:
    把解析后的数据写plist文件,通过plist文件可以直观的查看JSON的层次结构。
    [dictM writeToFile:@"/Users/文顶顶/Desktop/videos.plist" atomically:YES];

5)视频的简单播放
//0.需要导入系统框架
#import 

//1.拿到该cell对应的数据字典
XMGVideo *video = self.videos[indexPath.row];

NSString *videoStr = [@"http://120.25.226.186:32812" stringByAppendingPathComponent:video.url];

//2.创建一个视频播放器
MPMoviePlayerViewController *vc = [[MPMoviePlayerViewController alloc]initWithContentURL:[NSURL URLWithString:videoStr]];
//3.present播放控制器

[self presentViewController:vc animated:YES completion:nil];

3 字典转模型框架
1)相关框架
a.Mantle 需要继承自MTModel
b.JSONModel 需要继承自JSONModel
c.MJExtension 不需要继承,无代码侵入性

2)自己设计和选择框架时需要注意的问题
    a.侵入性
    b.易用性,是否容易上手
    c.扩展性,很容易给这个框架增加新的功能

3)MJExtension框架的简单使用
    //1.把字典数组转换为模型数组
        //使用MJExtension框架进行字典转模型
            self.videos = [XMGVideo mj_objectArrayWithKeyValuesArray:videoArray];

    //2.重命名模型属性的名称
    //第一种重命名属性名称的方法,有一定的代码侵入性
    //设置字典中的id被模型中的ID替换
    +(NSDictionary *)mj_replacedKeyFromPropertyName
    {
        return @{
                 @"ID":@"id"
                 };
    }

    //第二种重命名属性名称的方法,代码侵入性为零
        [XMGVideo mj_setupReplacedKeyFromPropertyName:^NSDictionary *{
            return @{
                     @"ID":@"id"
                     };
        }];

    //3.MJExtension框架内部实现原理-运行时

=======================================================================================================

2 XML解析

1 XML简单介绍
1) XML:可扩展标记语言
a.语法
b.XML文档的三部分(声明、元素和属性)
c.其它注意点(注意不能交叉包含、空行换行、XML文档只能有一个根元素等)

2) XML解析

    a.XML解析的两种方式
        001 SAX:从根元素开始,按顺序一个元素一个元素的往下解析,可用于解析大、小文件
        002 DOM:一次性将整个XML文档加载到内存中,适合较小的文件
    b.解析XML的工具
        001 苹果原生NSXMLParser:使用SAX方式解析,使用简单
        002 第三方框架
            libxml2:纯C语言的,默认包含在iOS SDK中,同时支持DOM和SAX的方式解析
            GDataXML:采用DOM方式解析,该框架由Goole开发,是基于xml2的

2 XML解析
1)使用NSXMLParser解析XML步骤和代理方法
//解析步骤:
//4.1 创建一个解析器
NSXMLParser *parser = [[NSXMLParser alloc]initWithData:data];
//4.2 设置代理
parser.delegate = self;
//4.3 开始解析
[parser parse];


    //1.开始解析XML文档
    -(void)parserDidStartDocument:(nonnull NSXMLParser *)parser

    //2.开始解析XML中某个元素的时候调用,比如
    -(void)parser:(nonnull NSXMLParser *)parser didEndElement:(nonnull NSString *)elementName namespaceURI:(nullable NSString *)namespaceURI qualifiedName:(nullable NSString *)qName

    //4.XML文档解析结束
    -(void)parserDidEndDocument:(nonnull NSXMLParser *)parser


2)使用GDataParser解析XML的步骤和方法
    //  0 配置环境
    // 001 先导入框架,然后按照框架使用注释配置环境
    // 002 GDataXML框架是MRC的,所以还需要告诉编译器以MRC的方式处理GDataXML的代码

    //  1 加载XML文档(使用的是DOM的方式一口气把整个XML文档都吞下)
        GDataXMLDocument *doc = [[GDataXMLDocument alloc]initWithData:data options:kNilOptions error:nil];

    //  2 获取XML文档的根元素,根据根元素取出XML中的每个子元素
      NSArray * elements = [doc.rootElement elementsForName:@"video"];

    //  3 取出每个子元素的属性并转换为模型
    for (GDataXMLElement *ele in elements) {

        XMGVideo *video = [[XMGVideo alloc]init];
        video.name = [ele attributeForName:@"name"].stringValue;
        video.length = [ele attributeForName:@"length"].stringValue.integerValue;
        video.url = [ele attributeForName:@"url"].stringValue;
        video.image = [ele attributeForName:@"image"].stringValue;
        video.ID = [ele attributeForName:@"id"].stringValue;

        //  4 把转换好的模型添加到tableView的数据源self.videos数组中
        [self.videos addObject:video];
    }

=======================================================================================================

3 NSURLSessionDownloadTask大文件下载

1 NSURLSessionDownloadTask实现大文件下载-Block

1)使用NSURLSession和NSURLSessionDownload可以很方便的实现文件下载操作
 /*
     第一个参数:要下载文件的url路径
     第二个参数:当接收完服务器返回的数据之后调用该block
     location:下载的文件的保存地址(默认是存储在沙盒中tmp文件夹下面,随时会被删除)
     response:服务器响应信息,响应头
     error:该请求的错误信息
     */
    //说明:downloadTaskWithURL方法已经实现了在下载文件数据的过程中边下载文件数据,边写入到沙盒文件的操作
    NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:url completionHandler:^(NSURL * __nullable location, NSURLResponse * __nullable response, NSError * __nullable error)
2)downloadTaskWithURL内部默认已经实现了变下载边写入操作,所以不用开发人员担心内存问题
3)文件下载后默认保存在tmp文件目录,需要开发人员手动的剪切到合适的沙盒目录
4)缺点:没有办法监控下载进度

2 使用NSURLSessionDownloadTask实现大文件下载-代理
1)创建NSURLSession并设置代理,通过NSURLSessionDownloadTask并以代理的方式来完成大文件的下载
//1.创建NSULRSession,设置代理
self.session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];

    //2.创建task
    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"];
    self.downloadTask = [self.session downloadTaskWithURL:url];

    //3.执行task
    [self.downloadTask resume];

2)常用代理方法的说明
        /*
     1.当接收到下载数据的时候调用,可以在该方法中监听文件下载的进度
     该方法会被调用多次
     totalBytesWritten:已经写入到文件中的数据大小
     totalBytesExpectedToWrite:目前文件的总大小
     bytesWritten:本次下载的文件数据大小
     */
    -(void)URLSession:(nonnull NSURLSession *)session downloadTask:(nonnull NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
    /*
     2.恢复下载的时候调用该方法
     fileOffset:恢复之后,要从文件的什么地方开发下载
     expectedTotalBytes:该文件数据的总大小
     */
    -(void)URLSession:(nonnull NSURLSession *)session downloadTask:(nonnull NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
    /*
     3.下载完成之后调用该方法
     */
    -(void)URLSession:(nonnull NSURLSession *)session downloadTask:(nonnull NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(nonnull NSURL *)location
    /*
     4.请求完成之后调用
     如果请求失败,那么error有值
     */
    -(void)URLSession:(nonnull NSURLSession *)session task:(nonnull NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error

3)实现断点下载相关代码
    //如果任务,取消了那么以后就不能恢复了
    //    [self.downloadTask cancel];

    //如果采取这种方式来取消任务,那么该方法会通过resumeData保存当前文件的下载信息
    //只要有了这份信息,以后就可以通过这些信息来恢复下载
    [self.downloadTask cancelByProducingResumeData:^(NSData * __nullable resumeData) {
        self.resumeData = resumeData;
    }];

    -----------
    //继续下载
    //首先通过之前保存的resumeData信息,创建一个下载任务
    self.downloadTask = [self.session downloadTaskWithResumeData:self.resumeData];

     [self.downloadTask resume];

4)计算当前下载进度
    //获取文件下载进度
    self.progress.progress = 1.0 * totalBytesWritten/totalBytesExpectedToWrite;

5)局限性
    01 如果用户点击暂停之后退出程序,那么需要把恢复下载的数据写一份到沙盒,代码复杂度更
    02 如果用户在下载中途未保存恢复下载数据即退出程序,则不具备可操作性

=======================================================================================================

4 NSURLSessionDataTask实现大文件下载

1 关于NSOutputStream的使用
//1. 创建一个输入流,数据追加到文件的屁股上
//把数据写入到指定的文件地址,如果当前文件不存在,则会自动创建
NSOutputStream *stream = [[NSOutputStream alloc]initWithURL:[NSURL fileURLWithPath:[self fullPath]] append:YES];

//2. 打开流
[stream open];

//3. 写入流数据
[stream write:data.bytes maxLength:data.length];

//4.当不需要的时候应该关闭流
[stream close];

2 关于网络请求请求头的设置(可以设置请求下载文件的某一部分)
//1. 设置请求对象
//1.1 创建请求路径
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"];

//1.2 创建可变请求对象
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

//1.3 拿到当前文件的残留数据大小
self.currentContentLength = [self FileSize];

//1.4 告诉服务器从哪个地方开始下载文件数据
NSString *range = [NSString stringWithFormat:@"bytes=%zd-",self.currentContentLength];
NSLog(@"%@",range);

//1.5 设置请求头
[request setValue:range forHTTPHeaderField:@"Range"];

3 NSURLSession对象的释放
-(void)dealloc
{
//在最后的时候应该把session释放,以免造成内存泄露
// NSURLSession设置过代理后,需要在最后(比如控制器销毁的时候)调用session的invalidateAndCancel或者resetWithCompletionHandler,才不会有内存泄露
// [self.session invalidateAndCancel];
[self.session resetWithCompletionHandler:^{

        NSLog(@"释放---");
    }];
}

4 优化部分

    01 关于文件下载进度的实时更新
    02 方法的独立与抽取

=======================================================================================================

5 文件压缩和解压缩

1)说明

    使用ZipArchive来压缩和解压缩文件需要添加依赖库(libz),使用需要包含SSZipArchive文件,如果使用cocoaPoads来安装框架,那么会自动的配置框架的使用环境

2)相关代码
    //压缩文件的第一种方式
    /*
     第一个参数:压缩文件要保存的位置
     第二个参数:要压缩哪几个文件
     */
    [SSZipArchive createZipFileAtPath:fullpath withFilesAtPaths:arrayM];

    //压缩文件的第二种方式
    /*
     第一个参数:文件压缩到哪个地方
     第二个参数:要压缩文件的全路径
     */
    [SSZipArchive createZipFileAtPath:fullpath withContentsOfDirectory:zipFile];

    //如何对压缩文件进行解压
    /*
     第一个参数:要解压的文件
     第二个参数:要解压到什么地方
     */
    [SSZipArchive unzipFileAtPath:unZipFile toDestination:fullpath];

=======================================================================================================

6 多值参数和中文输出

1 多值参数
/*
如果一个参数对应着多个值,那么直接按照"参数=值&参数=值"的方式拼接
*/
-(void)test
{
//1.确定URL
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/weather?place=Beijing&place=Guangzhou"];
//2.创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];

    //3.发送请求
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {

        //4.解析
        NSLog(@"%@",[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]);
    }];
}

2 如何解决字典和数组中输出乱码的问题
给字典和数组添加一个分类,重写descriptionWithLocale方法,在该方法中拼接元素格式化输出。
-(nonnull NSString *)descriptionWithLocale:(nullable id)locale

你可能感兴趣的:(基本JSON, XML解析,和大文件下载压缩)