我们从网络上某个服务器中获取到一个JSON数据,格式如下:(某游戏的分区选项)
/*
{
"fn": "艾欧尼亚",
"sn": "电信一",
"snEn": "dx1"
},.......
*/
/*
*******************RegionModel.h********************
/*
{
"fn": "艾欧尼亚",
"sn": "电信一",
"snEn": "dx1"
},.......
*/
/*
在工作中我们可能碰到一个问题。服务器开发人员可能不知道IOS或java等语言中的系统关键字,并使用了它。我们就会因系统报错而无法正常解析数据。
因此和服务器人员做好沟通是前提。但如果服务器人员坚持使用系统关键字的话,我们就只能做以下操作,来解决这个问题。
例:假设服务器传了如下内容
{"new":"asdfsdfasdf"}
new是系统关键词,我们无法直接设置属性调用
*/
#import <Foundation/Foundation.h>
#import "BaseModel.h"
@interface RegionModel : BaseModel //它继承于我们一会要创建的类。
//下面是个错误写法,如果用了new关键字做为属性会报错。不要写进程序
//@property (nonatomic, strong) NSString *new;
//假设服务器传过来的数据中fn是系统关键词(实际上并不是,就是举个粟子),所以属性名取fun1
@property (nonatomic, strong) NSString *fun1;//左边按服务器规则应该取名为fn
@property (nonatomic, strong) NSString *sn;
@property (nonatomic, strong) NSString *snEn;
@end
******************RegionModel.m*************************
#import "RegionModel.h"
@implementation RegionModel
//下面方法用来将字典中的Key,fn(我们之前假设它是系统关键字不能用)和fun1(换成这个系统就不会报错了,这个属性名可以自由取)进行互换。
- (NSDictionary *)specialKeys{
return @{@"fn":@"fun1"};//用fun1来替换fn
}
@end
************************BaseModel.h(上面的类继承它)*****************************
#import <Foundation/Foundation.h>
@protocol BaseModelDelegate <NSObject> //定义协议实现下面的解析方法
+(id)parse:(id)responseObj;
@end
@interface BaseModel : NSObject<BaseModelDelegate>//遵守协议
-(NSDictionary *)specialKeys;
@end
************************BaseModel.m****************************************
#import "BaseModel.h"
@implementation BaseModel
//重写以下方法,使得我们定义的类中属性名和服务器传回来的Key无法完全对上时,不会报错。
-(void)setValue:(id)value forUndefinedKey:(NSString *)key{
}
//下面方法在子类RegionModel.m中也有写,我们通常通过子类调用,会优先使用子类的方法。如果子类没写才会调用该方法返回空值。
- (NSDictionary *)specialKeys{
return nil;
}
//下面方法用来解析字典类的JSON返回数据
+ (id)parseDic:(id)responseObj{
id model = [self new];
//我们会通过子类调用这个方法。所以以下的specialKeys方法会优先调用子类中的。
//我们已经在RegionModel中写了一个例子方法。
NSDictionary * dic = [model specialKeys];
[responseObj enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
//如果遍历出的key在子类specialKeys方法中有写,那么
//以下判断语句作用是找出特定的Key值,并用我们定义的Key去替换它。
if ([dic objectForKey:key]) {
key = dic[key];
}
[model setValue:obj forKey:key];//把解析好的值加入model
}];
return model;
}
//下面方法用来解析数组类型的JSON返回数据
+ (NSArray *)parseArr:(NSArray *)responseObj{
NSMutableArray * arr = [NSMutableArray new];
//以下方法遍历JSON并对得到的obj进行解析
[responseObj enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
id obj1 = [self parseDic:obj];
[arr addObject:obj1];
}];
return [arr copy];
}
//以下方法会自动判断返回的JSON是哪种类型的数据。并根据情况解析
+ (id)parse:(id)responseObj{
id obj = responseObj;
if ([responseObj isKindOfClass:[NSDictionary class]]) {
obj = [self parseDic:responseObj];
return obj;
}
if ([responseObj isKindOfClass:[NSArray class]]) {
obj = [self parseArr:responseObj];
return obj;
}
//即使你没有进行JSON解析,该方法也能自动为你解析出来
//如你只是用NSSessionDataTask 得到一个data,把它传入该方法,就可以直接得到解析的数据
if ([responseObj isKindOfClass:[NSData class]]) {
NSError * error =nil;
id data = [NSJSONSerialization JSONObjectWithData:responseObj options:NSJSONReadingMutableLeaves|NSJSONReadingAllowFragments|NSJSONReadingMutableContainers error:&error];
//正常情况下,解析出来的一定是字典或数组或字符串类型
BOOL success = [data isKindOfClass:[NSArray class]]||[data isKindOfClass:[NSDictionary class]]||[data isKindOfClass:[NSString class]];
//如果序列化出问题了或结果类型不正确,断言NSAssert1最后的数字表示有几个参数,
NSAssert1(!error||success,@"JSON数据有问题 %s",__FUNCTION__);
return [self parse:data];
}
return obj;
}
@end
*************************下面是个实例********************************
#import "ViewController.h"
#import "RegionModel.h"//这是前面写的那个游戏大区的类
@interface UITableViewController ()
@property (nonatomic,strong) NSArray * regions;
@end
@implementation UITableViewController
- (void)viewDidLoad {
[super viewDidLoad];
//下面的path是某游戏的大区列表,相信你从名字看得出来是什么应用及游戏。解析出来是个数组类型的数据
NSString * path =@"http://lolbox.duowan.com/phone/apiServers.php?v=77&OSType=iOS8.2&versionName=2.1.7";
NSURLSessionDataTask * task = [[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:path] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
//我们没有解析得到的data,直接放入parse方法中解析。并用一个数组接收。(接收类型是根据服务器传的数据来确定的)
//如何判断接收数据类型呢?首先我们要了解JSON中包含的符号如下:
//{} 如果最外层是大括号,反回的一定是字典,有极少数情况是字符串。抓包的时候一眼就能看出来。中间有:号就是字典。
//[] 如果最外层是中括号,反回的一定是数组。
//例子:{["a":"b","c":12],"error":"0"},左边的JSON数据反回是个字典,中间又包含了数组。
_regions = [RegionModel parse:data];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}];
[task resume];
}