一、思路:
1、通过模型类型获得所有的属性和其类型
2、对获得的json进行处理。类型处理
3、考虑字典键值和模型属性名不一致的情况
4、添加code用于归档
5、补充JSON转字典、字典转JSON、字典转模型等接口
6、对处理过的properties做缓存
二、设计模式思考:
设计模式的选择---------继承、接口、抽象基类的选择。
在使用方便、高效率、低耦合之间抉择。
三、细节及实现
先把任务分解,实现各个部分细节,然后进行组合,当然我们要思考好,采用何种设计模式组装。先来看看各个部分的实现细节。
1.通过模型类型获得所有的属性和其类型,
 unsigned int outCount = 0;//获得Class c所有属性这里的c是[Model class]objc_property_t *properties = class_copyPropertyList(c, &outCount);for (int i = 0; i < outCount; i++) {objc_property_t propert = properties[i];//获得属性名NSString *key = @(property_getName(propert));//获得属性类型,如CGFloat、nonatomic、copy等信息NSString *type = @(property_getAttributes(propert));NSLog(@"key = %@ , type = %@", key, type);}

 Model模型如下

//属性}
typedef void(^block)();
@interface Model : NSObject
@property (nonatomic, copy) NSString *q_NSString;
@property (nonatomic, assign) CGFloat q_CGFloat;
@property (nonatomic, assign) CGRect q_CGRect;
@property (nonatomic, assign) double q_double;
@property (nonatomic, assign) int q_int;
@property (nonatomic, assign) BOOL q_bool;
@property (nonatomic, assign) float q_float;
@property (nonatomic, assign) short q_short;
@property (nonatomic, assign) long q_long;
@property (nonatomic, assign) long long q_longlong;
@property (nonatomic, assign) Point q_point;@property (nonatomic, strong) id q_id;
@property (nonatomic, weak) id<NSObject> q_delegate;
@property (nonatomic, copy) block q_block;
@property (nonatomic, strong) Model1 *q_model1;@property SEL q_SEL;
@property Class q_Class;
@property Ivar q_Ivar;
@property Method q_Method;

输出结果为

key = q_NSString , type = T@"NSString",C,N,V_q_NSString
key = q_CGFloat , type = Td,N,V_q_CGFloat
key = q_CGRect , type = T{CGRect={CGPoint=dd}{CGSize=dd}},N,V_q_CGRect
key = q_double , type = Td,N,V_q_double
key = q_int , type = Ti,N,V_q_int
key = q_bool , type = TB,N,V_q_bool
key = q_float , type = Tf,N,V_q_float
key = q_short , type = Ts,N,V_q_short
key = q_long , type = Tq,N,V_q_long
key = q_longlong , type = Tq,N,V_q_longlong
key = q_point , type = T{Point=ss},N,V_q_point
key = q_id , type = T@,&,N,V_q_id
key = q_delegate , type = T@"<NSObject>",W,N,V_q_delegate
key = q_block , type = T@?,C,N,V_q_block
key = q_model1 , type = T@"Model1",&,N,V_q_model1
key = q_SEL , type = T:,V_q_SEL
key = q_Class , type = T#,&,V_q_Class
key = q_Ivar , type = T^{objc_ivar=},V_q_Ivar
key = q_Method , type = T^{objc_method=},V_q_Method

将type用”,”分开,以T@"NSNumber",N,R,Vname  为例
在类中的声明为 let name: NSNumber =  NSNumber()
  • T@“NSNumber” 标记了属于什么类型
  • N            线程安全 相当与Objective-C中的nonmatic
  • R            不可变,R相当与Objective-C中的readonly,C相当于copy
  • Vname        去掉V,name就是变量名
通过对type进行处理就可以获得属性的类型。从而进行下一步处理。
注意点:class_copyPropertyList返回的仅仅是对象类的属性,class_copyIvarList返回类的所有属性和变量,在swift中如let a: Int? 是无法通过class_copyPropertyList返回的。
2.对type的处理使用方法可能不同,但是目的是一样的,就是为了从type字符串中区分出不同的类型
主要分为这几种:对象、协议、block、基本类型、结构体、自定义类型、Class、Ivar、Method、SEL、
MJExtension采用这样区分,在MJExtensionConst中这样定义
NSString *const MJPropertyTypeInt = @"i";
NSString *const MJPropertyTypeShort = @"s";
NSString *const MJPropertyTypeFloat = @"f";
NSString *const MJPropertyTypeDouble = @"d";
NSString *const MJPropertyTypeLong = @"l";
NSString *const MJPropertyTypeLongLong = @"q";
NSString *const MJPropertyTypeChar = @"c";
NSString *const MJPropertyTypeBOOL1 = @"c";
NSString *const MJPropertyTypeBOOL2 = @"b";
NSString *const MJPropertyTypePointer = @"*";NSString *const MJPropertyTypeIvar = @"^{objc_ivar=}";
NSString *const MJPropertyTypeMethod = @"^{objc_method=}";
NSString *const MJPropertyTypeBlock = @"@?";
NSString *const MJPropertyTypeClass = @"#";
NSString *const MJPropertyTypeSEL = @":";
NSString *const MJPropertyTypeId = @"@";

MJExtension采用对type进行字符串处理就能够区分出具体的类型
而在JSONModel采用 NSScanner,对类型进行处理。
比较下,个人觉得采用定义MJExtensionConst这样一个类来存放区分的值显得更加清晰。对于阅读源代码的人来说相对比较好。当然也可以结合起来使用
3.对获得的JSON进行类型处理。
  • 在JSON中为NSNumer,而propertytype为NSString,这种情况很常见。我们就需要处理一下,当propertytype为NSString,而在JSON中为NSNumber,就把NSNumber转化为NSString。
  • Readonly不需要赋值
  • nil处理
  • 可变和不可变处理
  • 模型就需要递归处理
  • NSString -> NSURL
  • 字符串转BOOL
  • 还有一些其他处理,以上的处理中也不是每个第三方都进行处理了
截取MJEextension中的一部分代码
  if ([value isKindOfClass:[NSStringclass]]) {if (propertyClass == [NSURL class]) {// NSString -> NSURL// 字符串转码value = [value mj_url];} else if (type.isNumberType) {NSString *oldValue = value;// NSString -> NSNumbervalue = [numberFormatter_ numberFromString:oldValue];// 如果是BOOLif (type.isBoolType) {// 字符串转BOOL(字符串没有charValue方法)// 系统会调用字符串的charValue转为BOOL类型NSString *lower = [oldValue lowercaseString];if ([lower isEqualToString:@"yes"] || [lower isEqualToString:@"true"]) {value = @YES;} else if ([lower isEqualToString:@"no"] || [lower isEqualToString:@"false"]) {value = @NO;}}}}

4.采用KVC赋值
setValue:forKey:
就不多说了
5.考虑字典键值和模型属性名不一致的情况
比如id、descripition不能作为属性名,当服务器使用时我们就需要另外构造名字;还有服务器一般使用user_name的命名方式,而OC中一般使用驼峰命名法即userName,那么我们就需要建立一对一的对应关系。
我想到有三种方法处理:
  • 采用继承,定义model基类,子类重写父类方法。
  • block设置block回调。
  • 可以采用抽象基类。
这个部分就需要我们好好考虑下了,因为涉及到我们如何提供接口,使用者如何使用的问题。需要考虑到使用是否方便、效率高低、低耦合是否高等问题。
先来说第一种,采用继承的方法,无疑的会带来高耦合的后果,当然在设计合理的情况下,大部分情况下使用起来更方便。
第二种采用block的方法。解决了继承带来的高耦合的成本。使用起来也很方便。MJEextension就是用这种方法。但是这种方法往往让我们在调用转化方法时调用block,些一大串的不同键值的转化。个人觉得不同键值的转化写在model里面比较好,而且在封装时,就有意识地引导使用者这么做
第三种方法使用抽象基类,定义一个协议,协议提供一个或者几个键值转化的方法。当model遵守并实现了此协议,就找到处理方法进行处理。这种作法避免了继承的高耦合的成本,也是使用者在model遵守协议,实现此方法。键值转化的方法写在model里面,避免转化的地方些太多东西。有时候还要写几遍。当然这样也有些缺点,就是每次有需要转化时(而且键值不同的情况很常见)需要引入,然后遵守协议。连续建几个model时,复制起来都不方便。
6.其他方面
(未测试) 对于SEL、Method 、Ivar不能使用KVC,如果考虑这些情况,需要进行判断。
在赋值时,设置可以忽略的属性
7.添加code用于归档
添加方法
- (id)initWithCoder:(NSCoder *)decoder
- (void)encodeWithCoder:(NSCoder *)encoder

一般是给NSObject添加分类,因为我们可以得到类的所有属性和对应的值。在两个方法中遍历属性,进行编码和解码。
方便做些缓存,很多的页面缓存就可以这样做。使用比较方便。
8.补充JSON转字典
+ (NSDictionary *)dictionaryWithJsonString:(NSString *)jsonString {if (jsonString == nil) {
    return nil;}NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];NSError *error;NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData        options:NSJSONReadingMutableContainerserror:&error];if(error) {NSLog(@"json解析失败:%@",error);return nil;}return dic;
}

此方法稍作变换,也可输出数组,主要看json格式。
9.字典转JSON
+ (NSString*)dictionaryToJson:(NSDictionary *)dic
{NSError *error = nil;NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dic                                  options:NSJSONWritingPrettyPrinted                                  error:&error];return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}

10.对处理过的properties一般需要做缓存
定义一个字典缓存,缓存处理过的properties,重复使用是不需要重复计算,用空间换时间。例如在归档的时候需要获得属性名列表,然后遍历归档、解挡。其他中间操作也需要用到properties。
水平有限,错误之处,欢迎大家指正。互相学习。转载请注明出处

转载于:https://www.cnblogs.com/booksky/p/5083649.html

JSON转Model内部实现解析相关推荐

  1. HandyJSON:Swift语言JSON转Model工具库

    背景 JSON是移动端开发常用的应用层数据交换协议.最常见的场景便是,客户端向服务端发起网络请求,服务端返回JSON文本,然后客户端解析这个JSON文本,再把对应数据展现到页面上. 但在编程的时候,处 ...

  2. Codable实现json转Model,是时候干掉HandyJSON了!

    自从开始使用Swift做项目,一直都在使用HandyJSON,不可否认,HandyJSON在Swift4.0以前是个好东西,也尝试过其它json转mode的工具,最终发现还是HandyJSON最好用. ...

  3. swift 访问oracle,Swift实现JSON转Model - HandyJSON使用讲解

    很多时候,我们从服务端请求下的数据都是Json格式,我们需要拿这些数据显示到我们的UI界面. 因此,我们的做法基本都会先将json转为方便使用的数据模型,或者也可以直接转字典解决. 在OC中,我们有很 ...

  4. android 生成泛型对象,java android解析多层含有泛型对象的json数据获取不到泛型类型解析失败解决办法...

    ####问题描述 * java 解析多层含有泛型对象的json数据获取不到泛型类型 * 如果将泛型改成实际的类型就能正常解析 * 如果不改成实际的类型泛型数据被解析成com.google.gson.i ...

  5. linux解析json指针,Linux下JSON通信协议的使用和解析

    JSON的使用 1.JSON通信协议的概念 2.JSON的语法 2.1JSON对象 2.2JSON数组 2.3JSON字符串 3.JSON的使用与解析 3.1JSON结构体 3.2JSON格式的使用 ...

  6. flex bison解析json文件_每秒解析千兆字节的 JSON 解析器开源了

    本文转自我们的网站 InfoQ,译者无明.除了推荐 simdjson 之外,还想测试一下微信平台编辑器的代码样式功能. 事实证明,微信文章的代码展示能力很强了.非常棒. 近日,GitHub 开源了一 ...

  7. linux服务器无法解析域名解决办法,Linux服务器内部无法解析域名

    Linux服务器内部无法解析域名 问题现象 Linux 服务器内部无法正常解析域名. 问题原因 可能的原因包括: DNS 设置问题 防火墙策略问题 NSCD 服务问题 处理办法 可以依次进行如下检查: ...

  8. Restful API 生成复杂Json数据结构及使用客户端解析该数据结构(三)

    前提说明:首先约定接口之前,需要约定接口的参数,接口参数包括输入参数和输出参数 输入参数:指接口调用时输入的参数 输出参数:即接口调用时返回的参数. 那么如果说,约定输入输出参数均需要采用Json结构 ...

  9. linux dns无法解析,Linux服务器内部无法解析域名

    Linux服务器内部无法解析域名 问题现象 Linux 服务器内部无法正常解析域名. 问题原因 可能的原因包括: DNS 设置问题 防火墙策略问题 NSCD 服务问题 处理办法 可以依次进行如下检查: ...

  10. kubernetes集群内部DNS解析原理

    kubernetes集群内部DNS解析原理 当kubernetes初始化完成后,在kube-system名称空间下会出现kube-dns的service服务与coredns的pod $ kubectl ...

最新文章

  1. Java Web之POI操作Excel2016模板
  2. 只需几步,U盘就能变“光驱”
  3. HDU 1232 - 畅通工程
  4. 如何发现 GitHub 上那些有趣好玩的项目?
  5. 面向对象程序设计(OOP)的六大原则
  6. 字节流写数据的两个小问题
  7. Moonlight已经可以下载,目前是0.6版
  8. SAP HANA,S/4HANA 和 SAP BTP 的辨析
  9. geoserver 3_SD 2-3/15 PR调速阀德国HAWE哈威
  10. Google 程序员消灭 Bug 的 5 大法宝!
  11. php更换wordpress用户头像,WordPress主题设置在前台页面添加用户头像教程
  12. 安装配置文件共享协议(SAMBA)
  13. 结构专业规范大全_建筑业最新规范大全!拿着手机看规范、查标准,超级方便...
  14. 都能看懂的嵌入式linux/android alsa_aplay alsa_amixer命令行使用方法
  15. Win11查看电脑磁盘分区格式的方法教学
  16. JNI/NDK入门指南之JNI数据类型,描述符详解
  17. 个人邮箱地址格式,如何能够正确的书写?
  18. 前端 css 自动生成,关于前端:利用Zeplin从设计图自动生成CSS提高前端样式开发效率...
  19. java rotateright_Java Tetris旋转
  20. 常用的web服务器有哪些

热门文章

  1. 如何使用 AirDrop 将 MAC 中的照片整理好,并上传到 iPhone 的相册中?
  2. MacBook 键盘出现故障,如何修复?
  3. freeCodeCamp 221题
  4. recyclerview简单实现单选多选反选全选
  5. SDUT OJ 3403 数据结构实验之排序六:希尔排序
  6. 《HTML5游戏编程核心技术与实战》——2.3 图像API
  7. “System.FormatException”类型的未经处理的异常在 System.IdentityModel.dll 中发生 其他信息: 十六进制字符串格式无效。...
  8. #openssl #爆重大漏洞heartbleed,危及两亿网民!!!
  9. uva 10246(变形floyd)
  10. [分享]如何开机后直接进入桌面