美文网首页iOS基础·OC高级篇我爱编程
iOS进阶之网络--JSON、Plist、Xml解析

iOS进阶之网络--JSON、Plist、Xml解析

作者: 一个默默无闻的程序猿 | 来源:发表于2018-04-11 10:47 被阅读59次

简介

网络数据传输通用的格式有JSON、Plist、Xml等。首先要清楚网络数据传输的逻辑:序列化和反序列化。序列化是指将数据转换成二进制数据流在网络中传输;反序列化是指将二进制数据流转化成我们能使用的数据,在OC中转换成数据对象,通过JSON、Plist、XML等数据解析完成反序列化。

  • JSON:轻量级的数据交换格式,目前移动开发绝大多数在使用这种格式。JSON出自于民间,语法是JavaScript对象标识的语法子集,本质上就是一种特殊的字符串,OC中的NSDictionary的快速包装的方法就是借鉴了JSON。JSON解析的三方框架有JSONKit、SBJson、TouchJson等一些古老的解析三方,这里只介绍苹果的NSJSONSerialization。
    JSON的格式:
[{
 "id":1,
 "username":"zhangsan",
"age":18
},
{
 "id":2,
"username":"xiaohua",
"age":null
 }]

[ ]保存数组,{ }保存对象,对应OC中的NSArray和NSDictionary结构很相近。

  • Plist:在实际的开发中,不管是Java后台、PHP后台,很少用到。Plist在苹果Mac、iOS中使用的比较多。
    Plist格式:


    Plist格式
  • XML:是曾经最流行的格式,出身名门,由W3C主推,微软和IBM推荐使用。xml是可扩展"标记"语言,格式
 <标记></标记>
 <标记>内容</标记>
 <标记 属性1=“值”,属性2=“值”>标记内容</标记>

解析方式:DOM、SAX
DOM MAC提供的解析方式,iOS无法使用,主要原因跟是内存开销太大;SAX是iOS提供的解析方式,内存开销小、速度快,但是只读,无法修改xml文件。这里着重介绍SAX解析。


数据解析

通过网络请求获取数据

 NSURL *url = [NSURL URLWithString:@"http://127.0.0.1/XXX"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {

}]

获取到不同格式的数据data,下面根据不同的数据格式,分别介绍其解析方式。

  1. JSON:
    NSJSONSerialization是iOS 5.0 推出的JSON解析,主要是将网络请求的二进制数据转换成NSArray或NSDictionary。
    在NSJSONSerialization头文件中对JSON的介绍如下:
An object that may be converted to JSON must have the following properties:
     - Top level object is an NSArray or NSDictionary
     - All objects are NSString, NSNumber, NSArray, NSDictionary, or NSNull
     - All dictionary keys are NSStrings
     - NSNumbers are not NaN or infinity

一个JSON对象应该具备以下属性
- 顶级节点是一个 NSArray或者 NSDictionary
- 所有的对象必须是 NSString, NSNumber, NSArray, NSDictionary, or NSNull
- 所有字典的Key必须是NSString
- NSNumber 不能使无理数或者空
解析

        /**
Options参数:
         NSJSONReadingMutableContainers = (1UL << 0),   容器节点是可变的
         NSJSONReadingMutableLeaves = (1UL << 1),       子节点是可变的
         NSJSONReadingAllowFragments = (1UL << 2)       允许顶级节点不是 NSArray或NSDictionary
         */
        id result = [NSJSONSerialization JSONObjectWithData:data options:1 error:NULL];
        NSLog(@"%@   %@",result,[result class]);
        for (NSDictionary *dic in result) {
            NSLog(@"==>%@",dic);
        }

result 可能是数组、字典,一般根据后台给的文档可以判断区分,最外层是[ ] 则result是一个数组,如果是{ } 则result是一个字典。

  1. Plist
    NSPropertyListSerialization解析
        //解析数据
        //NSJSONSerialization JSON 序列化
        //Plist 序列化 用 NSPropertyListSerialization
        /**
         NSPropertyListImmutable                    plist属性列表不可变
         NSPropertyListMutableContainers            容器可变
         NSPropertyListMutableContainersAndLeaves   容器和子节点都可变
         */
        id result = [NSPropertyListSerialization propertyListWithData:data options:0 format:NULL error:NULL];
        NSLog(@"%@   %@",result,[result class]);
  1. XML
    要解析的数据
<videos>
    <video videoId="1">
        <name>01.C语言-语法预览</name>
        <length>320</length>
        <videoURL>/itcast/videos/01.C语言-语法预览.mp4</videoURL>  
        <imageURL>/itcast/images/head1.png</imageURL>
        <desc>C语言-语法预览</desc>
        <teacher>二宝</teacher>
    </video>
</videos>

SAX解析过程包括5个步骤,其实通过NSXMLParser的代理可以充分体现,具体步骤分为
(1)打开文档
(2)开始节点
<Videos>最外层标记,是一个数组
<Video videoID="1">数组内的对象,标记内的扩展属性videoID也
可以理解为对象的一个属性
<name>01.c语言-语法预览</name>对象的属性
(3)发现一个节点的内容,可以将内容理解为对象的属性(一个节点的内容,可能会读取很多次)所以这里要进行字符串拼接
01.语言
(4)发现结束节点
</name>
(5)完成解析
在这里我们直接xml解析为Model,解析前首先要准备:
一个存放Model的数组(可变),
读取Model属性时拼接字符串用到的NSString(可变),
Model

// XML解析需要的素材
// 1.可变数组
@property (strong,   nonatomic) NSMutableArray *videos;
// 2.可变字符串
@property (strong,   nonatomic) NSMutableString *elementStr;
// 3.当前解析的节点 -- 模型
@property (strong, nonatomic) Video *currentVideo;
- (NSMutableString *)elementStr{
    if (!_elementStr) {
        _elementStr = [NSMutableString string];
    }
    return _elementStr;
}
- (NSMutableArray *)videos{
    if (!_videos) {
        _videos = [NSMutableArray array];
    }
    return _videos;
}
        //XML 解析 -- 耗时操作
        NSXMLParser *parser = [[NSXMLParser alloc]initWithData:data];
        //设置代理
        parser.delegate = self;
        //解析器解析
        [parser parse];

重点是在代理方法中整理数据

//1.打开文档
- (void)parserDidStartDocument:(NSXMLParser *)parser{
    NSLog(@"1.开始文档");
    //清空数组
    [self.videos removeAllObjects];
}
//2.开始节点 (Element - 元素) attributeDict 包含在开始节点内的扩展属性
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary<NSString *,NSString *> *)attributeDict{
    NSLog(@"2.开始节点: \n节点名称: %@  \n节点属性: %@",elementName,attributeDict);
    //先判断element是不是video,如果是就
    if ([elementName isEqualToString:@"video"]) {
        self.currentVideo = [[Video alloc]init];
        self.currentVideo.videoId = [NSString stringWithFormat:@"%ld",[attributeDict[@"videoId"] integerValue]];
    }
}
//3.发现节点内容
- (void)parser:(NSXMLParser *)parser foundCharacters:(nonnull NSString *)string{
    //拼接字符串
    NSLog(@"3.发现节点内容 =====> %@",string);
    [self.elementStr appendString:string];
}
//4.结束节点
/**
 KVC key value 键值编码
 cocoa 大招!!! kvc
 */
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
    NSLog(@"4.结束节点 %@",elementName);
    if ([elementName isEqualToString:@"video"]) {
        [self.videos addObject:self.currentVideo];
    }else if (![elementName isEqualToString:@"videos"]){
        [self.currentVideo setValue:self.elementStr forKey:elementName];
    }
    [self.elementStr setString:@""];
}
//5.结束文档
- (void)parserDidEndDocument:(NSXMLParser *)parser{
    NSLog(@"5.结束文档 %@ %@",parser,self.videos);
    
}
//6.出现错误
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError{
    NSLog(@"6.出现错误 %@",parseError);
}

相关文章

网友评论

本文标题:iOS进阶之网络--JSON、Plist、Xml解析

本文链接:https://www.haomeiwen.com/subject/vnxahftx.html