zoukankan      html  css  js  c++  java
  • JSONModel

    JSONModel 一个解析 JSON 数据的开源库,可以将 JSON 数据直接解析成自定义的 model ,其中对数据类型的检查和对数据类型的转换比较贴心。最近在项目中使用了以后觉得确实方便很多,推荐给大家。(PS:由于实现的原理在swift中并不支持,所以swift代码了解就可以)。

    国际惯例介绍下背景,曾经有一段时间一直要写接口,自然少不了解析获得的 JSON 数据,觉得真的是一种非常机械重复的过程,懵懵懂懂的写一个基于 runtime 的解析器,感觉用起来方便了不少,但是在错误处理和一些特殊情况下使用起来还是觉得有一些不灵活。多年以后偶然的机会又遇到了相同的工作,解析 JSON 数据,这次选择了相对讨巧的办法,使用成熟的开源库来解决。想起那句话“知其然,知其所以然”,决定深入的分析下,把我的理解也分享给大家。

    先从使用说起吧,使用 JSONModel 非常简单,只需要将你的 model 类继承自 JSONModel ,而同时 model 中的属性名又恰巧可以和 JSON 数据中的 key 名字一样的话,那么非常恭喜你,你的工作已经完成90%。如果你还有特殊需求实际上写起来也非常方便,我觉得完全可以覆盖日常90%的工作。其他的功能我们会在分析源码的时候看到。

    JSONModel 不只使用非常方便而且还会帮你检查 JSON 数据的完整性,如果 JSON 数据不完整的话是要返回 nil 的。它还提供了基本的数据类型转换,比如服务器错将数字传成字符串的话 JSONModel 也会帮你转换成你期望的类型。 

    先看一下文件结构,无视掉网络相关的类是这样的

    既然我们是继承自JSONModel,那我们就看看JSONModel

    头文件分成三部分: 1) Property protocol 2) AbstractJSONModelProtocol 3) JSONModel

    第一部分 Property protocol 

    1. @protocol Ignore 
    2. @end
    复制代码

    我们看到的只是4个空的协议  Ignore, Optional, Index, ConvertOnDemand ,分别对应的四种使用方法,忽略、可选、排序、延迟加载,粗看起来貌似都是空的协议没有什么实际的使用价值,但是我觉得正是作者代码精彩之处,我们都知道在 runtime 系统中,每一个对象实例都有一个isa指针(PS:新的 runtime 命名可能有些变化,但是原理相通)指向的是该实例变量的 Class 对象,进而得到该实例对象的所有信息。而 JSONModel 正是利用 runtime 系统的这个特性进行解析数据的,在Class对象中我们可以获得属性的相关描述也包括了其符合的协议,这样这四个空协议就起到了画龙点睛的作用,灵活度直线提高。比如在解析JSON的时候我们发现其某些属性符合Igonre就不会解析该属性,其他的协议同理。美中不足由于swift中对这种机制支持的并不友好所以JSONModel应该不太适用。 

    还有最后一点作者非常贴心的实现两个Category为了防止编译器的警告,大家也可以参考一下这种写法。

    第二部分 AbstractJSONModelProtocol 

    是 JSONModel 符合的抽象协议。如果你的类也符合该协议, 可以理解于是和JSONModel一样的,可以将不继承自 JSONModel 的类当作属性一起解析,一般还是推荐继承自 JSONModel 不然里面很多细节都要你自己去实现。

    第三部分 JSONModel 

    1. -(instancetype)initWithString:(NSString*)string error:(JSONModelError**)err; 
    2. -(instancetype)initWithString:(NSString *)string usingEncoding:(NSStringEncoding)encoding error:(JSONModelError**)err; 
    3.   -(instancetype)initWithDictionary:(NSDictionary*)dict error:(NSError **)err; 
    4.   -(instancetype)initWithData:(NSData *)data error:(NSError **)error; 
    复制代码

    四个初始化方法,实际上最后都是使用initWithDictionary来实现的。具体实现可以参考代码

    顺便提一下instancetype这个类型是苹果为了解决我们在继承的时候,返回类型的问题,如果你没有没有遇到或者不了解可以注意下。这样写避免了一直被诟病的反悔 id 类型,感兴趣可以自己查一下相关的资料。

    还有一些方便的方法暂时不提,还有几个可以重载的方法.

    1. +(JSONKeyMapper*)keyMapper; 
    复制代码

    keyMapper  这个是解决JSON中的key和属性名字对应不上时使用的。只对该类生效

    setGlobalKeyMapper 这个mapper和keyMapper作用一样只是对所有的JSONModel的子类都生效的一个对应关系

    propertyIsOptional 和propertyIsIgnored 是协议的方法版,设想你有1000个属性要写明这两个协议你会不会疯掉。也许直接返回一个YES或者NO就能解决问题对吧。

    举个例子

    命名自动匹配

    {
      "id": "123",
      "name": "Product name",
      "price": 12.95
    }
    @interface ProductModel : JSONModel
    @property (assign, nonatomic) int id;
    @property (strong, nonatomic) NSString* name;
    @property (assign, nonatomic) float price;
    @end
    
    @implementation ProductModel
    @end

    模型嵌套 (模型包含其他模型)

    {
      "order_id": 104,
      "total_price": 13.45,
      "product" : {
        "id": "123",
        "name": "Product name",
        "price": 12.95
      }
    }
    
    @interface OrderModel : JSONModel
    @property (assign, nonatomic) int order_id;
    @property (assign, nonatomic) float total_price;
    @property (strong, nonatomic) ProductModel* product;
    @end
    
    @implementation OrderModel
    @end

    模型集合

    {
      "order_id": 104,
      "total_price": 103.45,
      "products" : [
        {
          "id": "123",
          "name": "Product #1",
          "price": 12.95
        },
        {
          "id": "137",
          "name": "Product #2",
          "price": 82.95
        }
      ]
    }
    @protocol ProductModel
    @end
    @interface ProductModel : JSONModel
    @property (assign, nonatomic) int id;
    @property (strong, nonatomic) NSString* name;
    @property (assign, nonatomic) float price;
    @end
    
    @implementation ProductModel
    @end
    
    @interface OrderModel : JSONModel
    @property (assign, nonatomic) int order_id;
    @property (assign, nonatomic) float total_price;
    @property (strong, nonatomic) NSArray<ProductModel>* products;
    @end
    
    @implementation OrderModel
    @end

    注意: 尖括号后 NSArray 包含一个协议. 这跟Objective-C原生的泛型不是一个概念. 他们不会冲突, 但对于JSONModel来说,协议必须在一个地方声明.

    key映射

    {
      "order_id": 104,
      "order_details" : [
        {
          "name": "Product#1",
          "price": {
            "usd": 12.95
          }
        }
      ]
    }
    
    @interface OrderModel : JSONModel
    @property (assign, nonatomic) int id;
    @property (assign, nonatomic) float price;
    @property (strong, nonatomic) NSString* productName;
    @end
    
    @implementation OrderModel
    
    +(JSONKeyMapper*)keyMapper
    {
      return [[JSONKeyMapper alloc] initWithDictionary:@{
        @"order_id": @"id",
        @"order_details.name": @"productName",
        @"order_details.price.usd": @"price"
      }];
    }
    
    @end

    设置全局键映射(应用于所有model)

    [JSONModel setGlobalKeyMapper:[
        [JSONKeyMapper alloc] initWithDictionary:@{
          @"item_id":@"ID",
          @"item.name": @"itemName"
       }]
    ];

    设置下划线自动转驼峰

    {
      "order_id": 104,
      "order_product" : @"Product#1",
      "order_price" : 12.95
    }
    @interface OrderModel : JSONModel
    
    @property (assign, nonatomic) int orderId;
    @property (assign, nonatomic) float orderPrice;
    @property (strong, nonatomic) NSString* orderProduct;
    
    @end
    
    @implementation OrderModel
    
    +(JSONKeyMapper*)keyMapper
    {
      return [JSONKeyMapper mapperFromUnderscoreCaseToCamelCase];
    }
    
    @end

    可选属性 (就是说这个属性可以为null或者为空)

    {
      "id": "123",
      "name": null,
      "price": 12.95
    }
    @interface ProductModel : JSONModel
    @property (assign, nonatomic) int id;
    @property (strong, nonatomic) NSString<Optional>* name;
    @property (assign, nonatomic) float price;
    @property (strong, nonatomic) NSNumber<Optional>* uuid;
    @end
    
    @implementation ProductModel
    @end

    忽略属性 (就是完全忽略这个属性)

    {
      "id": "123",
      "name": null
    }
    @interface ProductModel : JSONModel
    @property (assign, nonatomic) int id;
    @property (strong, nonatomic) NSString<Ignore>* customProperty;
    @end
    
    @implementation ProductModel
    @end

    设置所有的属性为可选(所有属性值可以为空)

    @implementation ProductModel
    +(BOOL)propertyIsOptional:(NSString*)propertyName
    {
      return YES;
    }
    @end

    使用JSONModel自带的 HTTP 请求

    //add extra headers
    [[JSONHTTPClient requestHeaders] setValue:@"MySecret" forKey:@"AuthorizationToken"];
    
    //make post, get requests
    [JSONHTTPClient postJSONFromURLWithString:@"http://mydomain.com/api"
                                       params:@{@"postParam1":@"value1"}
                                   completion:^(id json, JSONModelError *err) {
    
                                       //check err, process json ...
    
                                   }];

    将model转化为字典或者json格式的字符串

    ProductModel* pm = [[ProductModel alloc] initWithString:jsonString error:nil];
    pm.name = @"Changed Name";
    
    //convert to dictionary
    NSDictionary* dict = [pm toDictionary];
    
    //convert to text
    NSString* string = [pm toJSONString];

    自定义数据的转换

    @implementation JSONValueTransformer (CustomTransformer)
    
    - (NSDate *)NSDateFromNSString:(NSString*)string {
        NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
        [formatter setDateFormat:APIDateFormat];
        return [formatter dateFromString:string];
    }
    
    - (NSString *)JSONObjectFromNSDate:(NSDate *)date {
        NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
        [formatter setDateFormat:APIDateFormat];
        return [formatter stringFromDate:date];
    }
    
    @end

    自定义处理指定的属性

    @interface ProductModel : JSONModel
    @property (assign, nonatomic) int id;
    @property (strong, nonatomic) NSString* name;
    @property (assign, nonatomic) float price;
    @property (strong, nonatomic) NSLocale *locale;
    @end
    
    @implementation ProductModel
    
    // Convert and assign the locale property
    - (void)setLocaleWithNSString:(NSString*)string {
        self.locale = [NSLocale localeWithLocaleIdentifier:string];
    }
    
    - (NSString *)JSONObjectForLocale {
        return self.locale.localeIdentifier;
    }
    
    @end

    自定义JSON校验

    @interface ProductModel : JSONModel
    @property (assign, nonatomic) int id;
    @property (strong, nonatomic) NSString* name;
    @property (assign, nonatomic) float price;
    @property (strong, nonatomic) NSLocale *locale;
    @property (strong, nonatomic) NSNumber <Ignore> *minNameLength;
    @end
    
    @implementation ProductModel
    
    - (BOOL)validate:(NSError *__autoreleasing *)error {
        BOOL valid = [super validate:error];
    
        if (self.name.length < self.minNameLength.integerValue) {
            *error = [NSError errorWithDomain:@"me.mycompany.com" code:1 userInfo:nil];
            valid = NO;
        }
    
        return valid;
    }
    
    @end



     

    其他:参考网址:http://blog.csdn.net/sinat_28032633/article/details/51496288

    http://www.cocoachina.com/ios/20150104/10821.html

  • 相关阅读:
    JSTL学习总结
    Spring 3 MVC: Create Hello World Application In Spring 3.0 MVC(reprint)
    如何查询端口号被哪个程序占用?
    php 共享内存
    php 消息队列
    php 快速fork出指定个子进程
    批量 kill mysql 中运行时间长的sql
    socket发送http请求
    TCP/IP、Http、Socket的区别
    文本协议和二进制协议
  • 原文地址:https://www.cnblogs.com/dreamDeveloper/p/6052929.html
Copyright © 2011-2022 走看看