美文网首页我爱编程
iOS JSON、XML解析技巧

iOS JSON、XML解析技巧

作者: 超人猿 | 来源:发表于2018-05-25 17:26 被阅读140次

    序言:

    以下实验效果会采用简单的方式呈现,读者可根据自身情况修改实验代码
    如果数据复杂需要自己调试,可看我另一篇文章搭建本地Apache服务器,道理是相通的! Mac 本地搭建Apache服务器

    一、JSON解析

    这里,我们需要先分析数据再做解析操作!

    1.准备的数据:在Safari浏览器访问https://www.crios.cn/test.json
    2.效果图如下

    json数据.png

    打开xcode->新建项目->新建Person模型装载json数据

    // 代码如下
    // 以下代码意义是根据数据来创建变量的
    #import <Foundation/Foundation.h>
    
    @interface Person : NSObject
    
    @property (copy, nonatomic) NSString *username;
    @property (strong, nonatomic) NSNumber *age;
    @property (strong, nonatomic) NSNumber *id;
    
    @end
    

    回到ViewContorller.m文件,添加以下代码

    /* 在头部导入Person类
    // Model
    #import "Person.h"
    */
    
    
        // 1.创建url
        NSURL *url = [NSURL URLWithString:@"https://www.crios.cn/test.json"];
        
        // 2.创建请求
        NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:10.0];
        
        // 3.创建NSURLSession
        NSURLSession *session = [NSURLSession sharedSession];
        
        // 4.创建NSURLSessionDataTask
        NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            if (!error) {
                
                NSArray *jsons = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
                
                NSMutableArray *results = [NSMutableArray array];
                for (NSDictionary *dic in jsons) {
                    Person *person = [[Person alloc] init];
                    [person setValuesForKeysWithDictionary:dic];
                    [results addObject:person];
                }
                NSLog(@"结果:%@", results);
                
            } else {
                NSLog(@"错误提示:%@",error);
            }
        }];
        
        // 5.开启任务(如果任务在挂起状态,这里会重新开启任务)
        [dataTask resume];
    

    setValuesForKeysWithDictionary意义:
        dic中的key会找Person类中的变量,一一对应,再将key对应的value赋值过去

    打印效果:

    上面无法打印模型的变量值,我们可在Person.m文件中添加以下代码:

    - (NSString *)description {
        
        return [NSString stringWithFormat:@"<%@:%p>{id:%@,name:%@,age:%@}",self.class,self,_id, _username, _age];
        
    }
    

    输出效果

    第一问,我们已设定好Person类中的变量,但我们定义好的Person类与服务器返回的数据不一致会出现何种情况,此时怎么办?
    image.png

    程序直接奔溃

    莫慌,直接在Person.m文件中添加如下代码

    // 代码块暂时不需要其他操作,可空着
    - (void)setValue:(id)value forUndefinedKey:(NSString *)key {
        
    }
    

    程序正常跑起来了。

    接着来,第二问,服务器返回的数据,有我们OC语言的关键字,赋值操作会出现一些无法预测的问题,这可咋办呢?

    解决这个问题首先先在Person.h文件中修改代码

    @property (copy, nonatomic) NSString *username;
    //@property (strong, nonatomic) NSNumber *age;
    // 将关键字修改成我们想要的模样
    @property (strong, nonatomic) NSNumber *ID;
    

    其次在Person.m文件中添加如下代码

    - (void)setValue:(id)value forUndefinedKey:(NSString *)key {
        
        if ([key isEqualToString:@"id"]) {
            self.ID = value;
        }
        
    }
    

    实验效果跟预期一样。成功了。🤡

    第三问,我们打印的数组中,出现的中文为乱码,这可咋办呢?

    新建NSArray Category


    在NSArray+Log.m文件中添加以下代码

    // 实际代码看这里,以下代码意义不解释,相信大家都看得懂
    - (NSString *)descriptionWithLocale:(id)locale {
    
        NSMutableString *resultStr = [NSMutableString stringWithString:@"🇨🇳(\n"];
    
        [self enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
    
            [resultStr appendFormat:@"\t%@\n",obj];
    
        }];
    
        [resultStr appendFormat:@")\n"];
        return resultStr;
    }
    

    结果输出:

    二、XML解析

    XML:
        XML是一种可扩展"标记"语言,特点是阅读方便,美观大方。
        解析方式:

    方式 介绍 特点
    DOM MAC提供的解析方法,iOS中无法直接使用 1.内存开销大
    2.可读可写
    3.将数据以树形结构加载到内存
    SAX 适用于iOS的解析方式 1.内存开销小
    2.只读
    3.速度快
    4.从上到下,顺序解析的过程

    行,我们来做实验。
    1)同样的,我们也需要分析数据再做解析操作!

    1.准备的数据:在谷歌浏览器访问http://www.crios.cn/persons.xml (数据也可从其他地方来,道理是一样的)
    2.效果图如下

    xml数据

    2)新建Person类,来装载数据,看了前面的json解析之后,这里会更容易明白。

    Person.h文件

    @property (strong, nonatomic)NSNumber *personId;
    @property (copy, nonatomic)NSString *height;
    @property (copy, nonatomic)NSString *age;
    @property (copy, nonatomic)NSString  *name;
    @property (copy, nonatomic)NSString  *sex;
    @property (copy, nonatomic)NSString  *desc;
    

    Person.m文件

    - (NSString *)description {
        
        return [NSString stringWithFormat:@"<%@:%p>{videoID:%@,name:%@,age:%@,height:%@,sex:%@,desc:%@}",[self class],self,self.personId,self.name,self.age,self.height,self.sex,self.desc];
        
    }
    - (void)setValue:(id)value forUndefinedKey:(NSString *)key {
          // 这里代码可空着,主要意义是防止服务器返回的数据与我们创建的Model类对应不上
    }
    

    4)ViewController.m文件

    
    // 1.导入Person 类
    #import "Person.h"
    
    // 2.遵守代理协议
    @interface ViewController ()<NSXMLParserDelegate>
    
    // 3.创建可变数组
    @property (strong, nonatomic) NSMutableArray *persons;
    // 4.创建可变字符串
    @property (strong , nonatomic) NSMutableString *elementStr;
    // 5.当前解析的节点模型
    @property (strong, nonatomic) Person *currentPerson;
    
    @end
    
    @implementation ViewController
    
    // MARK: 懒加载
    - (NSMutableArray *)persons {
        if (!_ persons) {
            _ persons = [[NSMutableArray alloc] init];
            
        }
        return _ persons;
    }
    
    - (NSMutableString *)elementStr {
        if (!_elementStr) {
            _elementStr = [[NSMutableString alloc] init];
            
        }
        return _elementStr;
    }
    
    
    
    @end
    
    
    1. 请求数据
        //1.url
        NSURL *url = [NSURL URLWithString:@"http://www.crios.cn/persons.xml"];
        
        // 2.创建请求
        NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:10.0];
        
        // 3.创建NSURLSession
        NSURLSession *session = [NSURLSession sharedSession];
        
        // 4.创建NSURLSessionDataTask
        NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            if (!error) {
                
                // XML解析
                NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithData:data];
            
                // 设置代理
                xmlParser.delegate = self;
            
                //解析器解析
                [xmlParser parse];
    
            }
        }];
        
        // 5.开启任务(如果任务在挂起状态,这里会重新开启任务)
        [dataTask resume];
    

    3)补充说明这里采用苹果原生NSXMLParser类来进行解析,主要通过6种代理方法来进行操作

    // 1.打开文档
    - (void)parserDidStartDocument:(NSXMLParser *)parser {
        NSLog(@"1.开始文档");
        
        //1.清空数组
        [self.persons removeAllObjects];
    }
    // 2.开始节点
    - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary<NSString *,NSString *> *)attributeDict {
        
        if ([elementName isEqualToString:@"person"]) {
            
            //1.新建模型
            self.currentPerson = [[Person alloc] init];
            
            //2设置personID的属性
            self.currentPerson.personId = @([attributeDict[@"personId"] intValue]);
              
        }
    
    }
    // 3.发现节点内容
    - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
        [self.elementStr appendString:string];
    }
    // 4.结束节点
    - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
    
        if ([elementName isEqualToString:@"person"]) {
            
            [self.persons addObject:self.currentPerson];
            
        } else if (![elementName isEqualToString:@"persons"])  {
    
            [self.currentPerson setValue:self.elementStr forKey:elementName];
       
        }
        //清空字符串
        [self.elementStr setString:@""];
    
    }
    // 5.结束解析
    - (void)parserDidEndDocument:(NSXMLParser *)parser {
          NSLog(@"结果:%@",self.persons);
    }
    // 6.错误处理
    - (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError {
     
          NSLog(@"错误描述:%@",parseError.description);
    
    }
    
    总结:
        实验结束,解析JSON这里为了便捷展示采用苹果原生NSJSONSerialization类解析。简单粗暴。
        网络请求不用老方法:NSURLConnection中的 sendAsynchronousRequest,直接采用NSURLSession。
        最后,模型的赋值可采用Runtime机制,效率会更高,这里不多讲,后面会专门写Runtime文章

    相关文章

      网友评论

        本文标题:iOS JSON、XML解析技巧

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