在日常的iOS开发中,总会进行数据的转换,比如请求服务端获取数据,解析数据,转换成对应的model,这个转换过程比较繁琐,重复工作较多,今天给大家介绍一个很好用的JSON转换库MJExtension。
MJExtension是一套字典和模型之间互相转换的超轻量级框架,利用runtime进行数据之间的转换,,使用简单无侵入,使用门槛较低,下面简单介绍下MJExtension的功能。
MJExtension的功能介绍
下面是MJExtension的功能,包括将JSON --> Model,Dictionary -->Model,Model -->JSON,JSON Array --> Model Array 等等功能。
JSON --> Model、Core Data Model
JSONString --> Model、Core Data Model
Model、Core Data Model --> JSON
JSON Array --> Model Array、Core Data Model Array
JSONString --> Model Array、Core Data Model Array
Model Array、Core Data Model Array --> JSON Array
MJExtension的使用
在MJExtension中,我们使用的api都在NSObject+MJKeyValue.h文件中,里面有详细的注释,这里就不多说了。下面我们就用微博的数据举例子,进行使用讲解
1.简单的字典 -> 模型(模型中的属性都是基本类型)
1)首先我们看下字典转模型,只需要一行代码就可以,十分简洁方便,这里需要注意下,model中的对应属性和字典中的key要相同,调用mj_objectWithKeyValues。
2)假如我们觉得MJUser中的属性命名有些歧义,不是很好,这时候我们想改一下,比如字典中的name叫jack,可能对应属性叫firstname更好,这时候,我们就需要在字典转模型的时候,进行属性名的替换,我们可以这么做,在MJUser.m文件中实现mj_replacedKeyFromPropertyName方法,方法如下图所示。
3)如果在转换过程中,我们只想对部分属性,进行转换,比如,你只需要firstname和icon,name可以使用如下方法进行设置,mj_allowedPropertyNames或者mj_ignoredPropertyNames,这两个方法可以看做白名单和黑名单的方法。
在MJUser.m文件中实现mj_allowedPropertyNames方法,即可只转换firstname和icon
4)如果在字典转model的过程中,你需要改变firstname的值,在每个 firstname前面加上一段文字,比如:“jack”变成我是“我的名字是jack”,这时候可以使用mj_newValueFromOldValue方法,下面在MJUser.m文件中实现mj_newValueFromOldValue方法,每次进行转换的时候,都可以在firstname前加上“我的名字是”,或者还可以做一些其它的处理。
5)如果在字典转换为model完成后,想做一些其它事情,可以使用mj_keyValuesDidFinishConvertingToObject方法进行。
2.复杂的字典 -> 模型
除了上面的简单字典转模型,还有些复杂的模型,比如字典中包含字典和数组,这种情况,我们怎么处理呢???
1)首先我们看一下,下面这种数据结构,复杂的字典 -> 模型 (模型里面包含了模型)
这是一个字典嵌套字典的结构,它需要转成一个模型嵌套模型的结构
只需要调用mj_objectWithKeyValues方法即可,跟简单模型使用方法一致,其它都不需要我们做,至于模型嵌套模型这种结构的转换时怎么实现的,我会在下一部分,讲解原理的时候说到。
2)下面我们看一下,复杂的字典 -> 模型 (模型的数组属性里面又装着模型)
我们可以看到,上面MJStatusResult模型中的statuses属性和ads属性,都是一个array,而array中的每一个元素都是一个模型,比如statuses中是一个MJStatus模型,ads中是MJAd模型,在进行MJStatusResult模型的转换过程中,我们需要设置数组属性的模型的类型,这时候我们需要用到mj_objectClassInArray方法,指定数组属性中元素的模型类型,具体用法如下:
3.模型 -> 字典
模型转字典主要有一下几个方法,用法类似,这里就不多讲了,大家可以看下源码。
MJExtension的原理
MJExtension中类说明
在分析原理之前,我们先看下文件的MJExtension的文件组成,以及每个文件的作用。
1.MJExtension.h 只是引用了一些头文件,方便管理。
2.MJExtensionConst.h和MJExtensionConst.m 声明了一些宏定义,用来抛出异常和做判断用的,同时声明和定义了一些属性类型。
3.MJFoundation.h和MJFoundation.m文件主要是用来判断某个类是否是来自Foundation库中的基本类型,在进行字典和模型的转换过程中,会根据这个类来判断是否是模型嵌套模型的结构。
4.MJProperty.h和MJProperty.m是一个记录属性信息的类,包含属性的的所有信息,比如,属性类型,名字,来源等等。
5.MJPropertyKey.h和MJPropertyKey.m提供一个根据属性key,返回属性值的方法。
6.MJPropertyType.h和MJPropertyType.m是记录一个属性的类型,比如MJProperty中属性类型就是用MJPropertyType来记录的,MJPropertyType类中包含类型标识符,是否为基本数字类型,来源框架,是否支持KVC等等,在字典和模型转换中,提供属性类型信息。
7.NSObject+MJClass.h和NSObject+MJClass.m用来遍历所有类,并进行黑名单和白名单的管理。
8.NSObject+MJCoding.h和NSObject+MJCoding.m主要是对属性值进行归档和解档,并且可以对属性名字添加黑名单和白名单。
9.NSObject+MJKeyValue.h和NSObject+MJKeyValue.m类中提供了所有模型和字典之间转换的接口,上面讲的使用方法,都是来自与这个类的接口,我们需要熟悉里面的所有接口,以便可以更好的使用它。
10.NSObject+MJProperty.h和NSObject+MJProperty.m是遍历模型所有成员,获取模型中的属性,用MJProperty类保存属性,用于进行字典和模型转换。
11.NSString+MJExtension.h和NSString+MJExtension.m是用来进行属性名字转换的类,比如讲驼峰格式的类名,转换为下划线的命名格式。
MJExtension模型字典转换过程
1.使用runtime 获取模型的所有属性 ,转换成MJProperty属性模型列表
2.根据MJProperty中的属性类型,对属性进行处理,获取属性值。
3. 根据属性名字,通过KVC对属性赋值。
1.runtime如何获取所有属性
首先我们看下MJExtension的源码,NSObject+MJProperty类中的+ (NSMutableArray *)properties方法。
1)首先使用runtime中的class_copyPropertyList方法,获取类的所有属性,class_copyPropertyList方法会返回一个objc_property_t类型的对象,这是一个结构体,如下所示
objc_property是一个结构体,是一个内置的类型,与之关联的还有一个objc_property_attribute_t,它是属性的attribute,也就是其实是对属性的详细描述,包括属性名称、属性编码类型、原子类型/非原子类型等。它的定义如下:
2)然后遍历每个成员,将objc_property_t中的属性,转换成MJProperty对象,转换过程如下:
首先使用property_getName获取属性名,然后使用property_getAttributes获取objc_property_attribute_t,为一个字符串的结果,里面包含属性名称、属性编码类型、原子类型/非原子类型等,比如:NSString类型的属性,对应的attrs为T@"NSString",C,N,V_icon。下面我解释一下这个字符串对应的意思:
T 表示属性的类型 类型为基本对象类型和基本数据类型,基本对象类型的value为该对象类型名字 如NSArray、NSString、NSMutableDictionary 等
C 表示该属性为copy ;为&表示属性为strong;W表示属性为weak;空 表示属性为assgin
N表示为非原子属性
V表示属性的名字 此时value为加了下划线的属性名字
最后会根据property_getAttributes信息生成属性对象的type。
2.根据MJProperty中的属性类型,对属性进行处理,获取属性值。
根据属性类型,对属性进行处理主要在- (instancetype)mj_setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context 方法中进行的,如下所示:
从上面代码可以看出
1)通过valueInObject获取属性值value,
2)获取到值以后,做了一些特殊处理
将value由可变对象转换成不可变类型
如果value是模型,则继续将value转换成模型。
如果模型类型是NSString类型,会将属性类型中的NSNumber和NSURL转换为NSString。
如果模型类型是NSURL类型,会将属性类型中NSString转换为NSURL。
如果模型类型是NSNumber类型,会将属性类型中NSString转换为NSNumber。
如果模型类型是NSNumber类型,会将属性类型中NSString转换为NSNumber。
如果模型类型是BOOL类型,会将字符串和yes,true,no,false进行匹配,转换为BOOL类型
如果模型类型和属性类型不匹配,会将value设置为nil。
3. 根据属性名字,通过KVC对属性赋值。
接下来,会使用属性值value,为属性赋值,如下所示:
首先会判断是否支持KVC,如果支持会给属性赋值。
到这里就讲解完了MJExtension的使用和原理