我爱编程

iOS JSON、XML解析技巧

2018-05-25  本文已影响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文章
上一篇下一篇

猜你喜欢

热点阅读