我们从网络上某个服务器中获取到一个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];
}

*****