前言
如今基本都是客户端请求服务器的模式,数据传递方式基本都使用JSON的字符串方式。然而当我们获取了服务器发来的字符串时,需要将其转换成我们的数据模型。由于手动转换代码的繁琐,就有了一些自动化转换的第三方开源库。其中比较有名的有YYModel,JSONModel,Mantle,MJExtension。今天主要说明的是JSONModel的源码。
上述均说明一点:懒人永远都是社会进步的核心动力。。。
JSONModel的使用
我们从服务器请求到了如下的JSON数据:
{@"name":@"xxx",@"age":@"xxx",@"sex":@"xxx"}
我们只需要建立如下Model类:
#import "JSONModel.h"
@interface MyModel : JSONModel
@property (strong, nonatomic) NSString * name;
@property (strong, nonatomic) NSString * age;
@property (strong, nonatomic) NSString * sex;
@end
随后进行如下操作:
JSONModelError *error = nil;
MyModel *myModel = [[MyModel alloc] initWithString:jsonString error:&error];
JSONModel会自动将jsonString转换成我们所需要的数据模型。此外还有其他方法以便使用:
- (instancetype)initWithDictionary:(NSDictionary *)dict error:(NSError **)err;
- (instancetype)initWithData:(NSData *)data error:(NSError **)error;
- (instancetype)initWithString:(NSString *)string error:(JSONModelError **)err;
- (instancetype)initWithString:(NSString *)string usingEncoding:(NSStringEncoding)encoding error:(JSONModelError **)err;
上述初始化方法都大相径庭,如果上述方法传入的参数均合法,那么所定义的所有属性都会与该JSON值相匹配,同时JSONModel会转成所需要的数据类型。(通过JSONValueTransformer实现,有兴趣的可以去看下源码)
下面举出自动转换映射例子:
1.命名自动匹配
{
"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
2.model中包含其他model
{
"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
3.model中包含其他model的集合
{
"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
4.键值对映射,此可以解决JSON串中含有系统关键字(如id,description等),需使用键值映射转换(JSONKeyMapper)。
{
"id": 104,
"description" : [
{
"name": "Product#1",
"price": {
"usd": 12.95
}
}
]
}
@interface OrderModel : JSONModel
@property (assign, nonatomic) int order_id;
@property (assign, nonatomic) float price;
@property (strong, nonatomic) NSString* productName;
@end
@implementation OrderModel
+(JSONKeyMapper*)keyMapper
{
return [[JSONKeyMapper alloc] initWithDictionary:@{
@"id": @"order_id",
@"description.name": @"productName",
@"description.price.usd": @"price"
}];
}
@end
5.将JSON串中带下划线的转换成首字母大写
<pre>
+(<em><strong>JSONKeyMapper</strong> *)keyMapper {
return [<em><strong>JSONKeyMapper</strong> mapperFromUnderscoreCaseToCamelCase];
}
</pre>
6.二级转换
{
"order_id": 104,
"total_price": 103.45,
"products" : [
{
"productsId": "123",
"name": "Product #1",
"price": 12.95
},
{
"productsId": "137",
"name": "Product #2",
"price": 82.95
}
]
}
@protocol ProductModel
@end
@interface ProductModel : JSONModel
@property (assign, nonatomic) int productsId;
@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, ConvertOnDemand>* products;
@end
@implementation OrderModel
@end
<pre>ps:
ConvertOnDemand:懒加载
Optional:可为空
Ignored:可忽视此属性
</pre>
JSONModel的源码解析
NSError *error = nil;
MyJSONModel *model = [[MyJSONModel alloc] initWithDictionary:dict error:&error];
以上是数据转换的代码,一句话就可以实现字典转模型的操作,我们来看下源码。
-(id)initWithDictionary:(NSDictionary*)dict error:(NSError**)err
{
//check for nil input
if (!dict) {
if (err) *err = [JSONModelError errorInputIsNil];
return nil;
}
//invalid input, just create empty instance
if (![dict isKindOfClass:[NSDictionary class]]) {
if (err) *err = [JSONModelError errorInvalidDataWithMessage:@"Attempt to initialize JSONModel object using initWithDictionary:error: but the dictionary parameter was not an 'NSDictionary'."];
return nil;
}
//create a class instance
self = [self init];
if (!self) {
//super init didn't succeed
if (err) *err = [JSONModelError errorModelIsInvalid];
return nil;
}
//check incoming data structure
if (![self __doesDictionary:dict matchModelWithKeyMapper:self.__keyMapper error:err]) {
return nil;
}
//import the data from a dictionary
if (![self __importDictionary:dict withKeyMapper:self.__keyMapper validation:YES error:err]) {
return nil;
}
//run any custom model validation
if (![self validate:err]) {
return nil;
}
//model is valid! yay!
return self;
}
上述代码分为以下几个部分
- 判断传入字典是否为空,为空返回nil
- 判断传入参数是否为字典,非字典返回nil
- 初始化JSONModel模型对象,初始化失败返回nil
3.1 使用<strong>runtime
</strong>的<strong>objc_getAssociatedObject
</strong>判断<strong>model
</strong>的<strong>kClassPropertiesKey
</strong>是否映射过。
3.2 若未映射调用<strong>[self __inspectProperties];
</strong>进行对<strong>kClassPropertiesKey
</strong>参数的映射。
3.3 与前者类似映射<strong>kMapperObjectKey
</strong>
static const char * kMapperObjectKey;
static const char * kClassPropertiesKey;
-(id)init {
self = [super init];
if (self) {
[self __setup__];
}
return self;
}
-(void)__setup__ {
//3.1 所做的事
if (!objc_getAssociatedObject(self.class, &kClassPropertiesKey)) {
//3.2 所做的事
[self __inspectProperties];
}
//3.3 所做的事
id mapper = [[self class] keyMapper];
if ( mapper && !objc_getAssociatedObject(self.class, &kMapperObjectKey) ) {
objc_setAssociatedObject(
self.class,
&kMapperObjectKey,
mapper,
OBJC_ASSOCIATION_RETAIN // This is atomic
);
}
}
#pragma mark - key mapping
+(JSONKeyMapper*)keyMapper
{
return nil;
}
-(void)__inspectProperties {
NSMutableDictionary* propertyIndex = [NSMutableDictionary dictionary];
Class class = [self class];
NSScanner* scanner = nil;
NSString* propertyType = nil;
//
while (class != [JSONModel class]) {
......
class = [class superclass];
}
//3.2 映射,连接二者的关系
objc_setAssociatedObject(
self.class,
&kClassPropertiesKey,
[propertyIndex copy],
OBJC_ASSOCIATION_RETAIN
);
}
4.调用 <strong>[self __doesDictionary:dict matchModelWithKeyMapper:self.__keyMapper error:err]
</strong>确保<strong>dict
</strong>是<strong>keyMapper
</strong>的子集。
5.调用<strong>[self __importDictionary:dict withKeyMapper:self.__keyMapper validation:YES error:err]
</strong>来查值。然后将解析的值赋给<strong>Model
</strong>的<strong>property
</strong>。
6.调用<strong>[self validate:err]
</strong>,用户自定义重写<strong>-(BOOL)validate:(NSError**)error
</strong>验证。
初始化JSONModel模型对象
<strong>[self init]
</strong> 调用 <strong>[self __setup__]
</strong> 调用 <strong>[self __inspectProperties]
</strong>。
- <strong>
[self __setup__]
</strong> 所做的就是建立映射关系表,存放在全局变量中。这里用到了<strong>runtime
</strong>的<strong>objc_getAssociatedObject
</strong>,动态的给self这个模型类添加了一个属性。 - <strong>
[self __inspectProperties]
</strong> 为具体实现。
-(void)__inspectProperties {
NSMutableDictionary* propertyIndex = [NSMutableDictionary dictionary];
//temp variables for the loops
Class class = [self class];
NSScanner* scanner = nil;
NSString* propertyType = nil;
// inspect inherited properties up to the JSONModel class
while (class != [JSONModel class]) {
unsigned int propertyCount;
//通过class_copyPropertyList获取属性列表,得到objc_property_t数组
objc_property_t *properties = class_copyPropertyList(class, &propertyCount);
//loop over the class properties
//循环遍历objc_property_t数组
for (unsigned int i = 0; i < propertyCount; i++) {
JSONModelClassProperty* p = [[JSONModelClassProperty alloc] init];
//获取属性property的名字
objc_property_t property = properties[i];
const char *propertyName = property_getName(property);
p.name = @(propertyName);
//获取属性property的描述attributes(这个描述为字符串)
const char *attrs = property_getAttributes(property);
NSString* propertyAttributes = @(attrs);
NSArray* attributeItems = [propertyAttributes componentsSeparatedByString:@","];
//通过属性的描述判断
//ignore read-only properties
if ([attributeItems containsObject:@"R"]) {
continue; //to next property
}
//check for 64b BOOLs
if ([propertyAttributes hasPrefix:@"Tc,"]) {
//mask BOOLs as structs so they can have custom converters
p.structName = @"BOOL";
}
//使用NSScanner扫描之前获得的属性描述(字符串)
scanner = [NSScanner scannerWithString: propertyAttributes];
[scanner scanUpToString:@"T" intoString: nil];
[scanner scanString:@"T" intoString:nil];
//解析类
//check if the property is an instance of a class
if ([scanner scanString:@"@\"" intoString: &propertyType]) {
[scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@"\"<"]
intoString:&propertyType];
p.type = NSClassFromString(propertyType);
p.isMutable = ([propertyType rangeOfString:@"Mutable"].location != NSNotFound);
p.isStandardJSONType = [allowedJSONTypes containsObject:p.type];
//read through the property protocols
while ([scanner scanString:@"<" intoString:NULL]) {
NSString* protocolName = nil;
[scanner scanUpToString:@">" intoString: &protocolName];
if ([protocolName isEqualToString:@"Optional"]) {
p.isOptional = YES;
} else if([protocolName isEqualToString:@"Index"]) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
p.isIndex = YES;
#pragma GCC diagnostic pop
objc_setAssociatedObject(
self.class,
&kIndexPropertyNameKey,
p.name,
OBJC_ASSOCIATION_RETAIN // This is atomic
);
} else if([protocolName isEqualToString:@"Ignore"]) {
p = nil;
} else {
p.protocol = protocolName;
}
[scanner scanString:@">" intoString:NULL];
}
}
//解析结构体
//check if the property is a structure
else if ([scanner scanString:@"{" intoString: &propertyType]) {
[scanner scanCharactersFromSet:[NSCharacterSet alphanumericCharacterSet]
intoString:&propertyType];
p.isStandardJSONType = NO;
p.structName = propertyType;
}
//其余的基本数据类型,int等
//the property must be a primitive
else {
//the property contains a primitive data type
[scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@","]
intoString:&propertyType];
//get the full name of the primitive type
propertyType = valueTransformer.primitivesNames[propertyType];
if (![allowedPrimitiveTypes containsObject:propertyType]) {
//type not allowed - programmer mistaken -> exception
@throw [NSException exceptionWithName:@"JSONModelProperty type not allowed"
reason:[NSString stringWithFormat:@"Property type of %@.%@ is not supported by JSONModel.", self.class, p.name]
userInfo:nil];
}
}
//判断是否为optional或者ignored
NSString *nsPropertyName = @(propertyName);
if([[self class] propertyIsOptional:nsPropertyName]){
p.isOptional = YES;
}
if([[self class] propertyIsIgnored:nsPropertyName]){
p = nil;
}
//判断是不是有protocol
Class customClass = [[self class] classForCollectionProperty:nsPropertyName];
if (customClass) {
p.protocol = NSStringFromClass(customClass);
}
//few cases where JSONModel will ignore properties automatically
if ([propertyType isEqualToString:@"Block"]) {
p = nil;
}
//add the property object to the temp index
//设置相对应属性的值
if (p && ![propertyIndex objectForKey:p.name]) {
[propertyIndex setValue:p forKey:p.name];
}
}
free(properties);
//ascend to the super of the class
//(will do that until it reaches the root class - JSONModel)
class = [class superclass];
}
//finally store the property index in the static property index
objc_setAssociatedObject(
self.class,
&kClassPropertiesKey,
[propertyIndex copy],
OBJC_ASSOCIATION_RETAIN // This is atomic
);
}
简单来说
通过<strong>class_copyPropertyList
</strong>获取属性列表,得到<strong>objc_property_t
</strong>数组。
You can use the functions class_copyPropertyList and protocol_copyPropertyList to retrieve an array of the properties associated with a class (including loaded categories) and a protocol respectively
通过遍历<strong>objc_property_t
</strong>调用<strong>property_getName
</strong>获得名称,<strong>property_getAttributes
</strong>获得属性的描述(字符串)。
You can use the property_getAttributes function to discover the name and the @encode type string of a property.
在通过NSScanner扫描属性的描述(@encode,关于描述请戳我这里跳转官方文档),再使用<strong>JSONModelClassProperty
</strong>存储信息。
最后<strong>class = [class superclass]
</strong>获取父类,如果父类是JSONModel的子类,继续进行上述三步。
最后的最后<strong>objc_setAssociatedObject
</strong>。
字典MatchingModel确保是KeyMapper的子集
-(BOOL)__doesDictionary:(NSDictionary*)dict matchModelWithKeyMapper:(JSONKeyMapper*)keyMapper error:(NSError**)err {
//check if all required properties are present
NSArray* incomingKeysArray = [dict allKeys];
NSMutableSet* requiredProperties = [self __requiredPropertyNames].mutableCopy;
NSSet* incomingKeys = [NSSet setWithArray: incomingKeysArray];
//transform the key names, if necessary
if (keyMapper || globalKeyMapper) {
NSMutableSet* transformedIncomingKeys = [NSMutableSet setWithCapacity: requiredProperties.count];
NSString* transformedName = nil;
//loop over the required properties list
for (JSONModelClassProperty* property in [self __properties__]) {
transformedName = (keyMapper||globalKeyMapper) ? [self __mapString:property.name withKeyMapper:keyMapper] : property.name;
//check if exists and if so, add to incoming keys
id value;
@try {
value = [dict valueForKeyPath:transformedName];
}
@catch (NSException *exception) {
value = dict[transformedName];
}
if (value) {
[transformedIncomingKeys addObject: property.name];
}
}
//overwrite the raw incoming list with the mapped key names
incomingKeys = transformedIncomingKeys;
}
//check for missing input keys
if (![requiredProperties isSubsetOfSet:incomingKeys]) {
//get a list of the missing properties
[requiredProperties minusSet:incomingKeys];
//not all required properties are in - invalid input
JMLog(@"Incoming data was invalid [%@ initWithDictionary:]. Keys missing: %@", self.class, requiredProperties);
if (err) *err = [JSONModelError errorInvalidDataWithMissingKeys:requiredProperties];
return NO;
}
//not needed anymore
incomingKeys= nil;
requiredProperties= nil;
return YES;
}
整个func就做了两件事:
- 得到传入的json数据<strong>
dict
</strong>。 - <strong>
dict
</strong>和之前得到的<strong>objc_property_t
</strong>数组对比Matching 。
需要注意的有:
- 如果得到的<strong>
objc_property_t
</strong>数组是<strong>dict
</strong>的超集,说明我们的Model中有属性是赋不到值的,往往程序就会crash在这里。
解决方法,属性后加入<strong><Optional>
</strong>表示可以为空。或者<strong><em>设置所有属性为可选</strong>。
@implementation ProductModel
+(BOOL)propertyIsOptional:(NSString*)propertyName {
return YES;
}
@end
- 在json数据中例如有字段名为id或者description的语言关键字,语法中式不能这么写的
@property (assign, nonatomic) int id;
只能写这种
@property (assign, nonatomic) int xxxID;
而这么写了在此func种就会由于<strong>objc_property_t
</strong>中不包含<strong>xxxID
</strong>crash,于是就有了keyMapper可以避免这个问题。
{
"id": 104,
"description" : [
{
"name": "Product#1",
"price": {
"usd": 12.95
}
}
]}
@interface OrderModel : JSONModel
@property (assign, nonatomic) int order_id;
@property (assign, nonatomic) float price;
@property (strong, nonatomic) NSString* productName;
@end
@implementation OrderModel
+(JSONKeyMapper *)keyMapper {
return [[JSONKeyMapper alloc] initWithDictionary:@{
@"id": @"order_id",
@"description.name": @"productName",
@"description.price.usd": @"price"
}];
}
@end
赋值给Model
-(BOOL)__importDictionary:(NSDictionary*)dict withKeyMapper:(JSONKeyMapper*)keyMapper validation:(BOOL)validation error:(NSError**)err
{
//loop over the incoming keys and set self's properties
//从对象的properties数组中,循环到dict中取值
//通过KVC操作
for (JSONModelClassProperty* property in [self __properties__]) {
//convert key name to model keys, if a mapper is provided
//使用keyMapper的映射找对对应的字典属性的keyPath,获取jsonValue
NSString* jsonKeyPath = (keyMapper||globalKeyMapper) ? [self __mapString:property.name withKeyMapper:keyMapper] : property.name;
//general check for data type compliance
id jsonValue;
@try {
jsonValue = [dict valueForKeyPath: jsonKeyPath];
}
@catch (NSException *exception) {
jsonValue = dict[jsonKeyPath];
}
//check for Optional properties
if (isNull(jsonValue)) {
//skip this property, continue with next property
if (property.isOptional || !validation) continue;
if (err) {
//null value for required property
NSString* msg = [NSString stringWithFormat:@"Value of required model key %@ is null", property.name];
JSONModelError* dataErr = [JSONModelError errorInvalidDataWithMessage:msg];
*err = [dataErr errorByPrependingKeyPathComponent:property.name];
}
return NO;
}
Class jsonValueClass = [jsonValue class];
BOOL isValueOfAllowedType = NO;
for (Class allowedType in allowedJSONTypes) {
if ( [jsonValueClass isSubclassOfClass: allowedType] ) {
isValueOfAllowedType = YES;
break;
}
}
if (isValueOfAllowedType==NO) {
//type not allowed
JMLog(@"Type %@ is not allowed in JSON.", NSStringFromClass(jsonValueClass));
if (err) {
NSString* msg = [NSString stringWithFormat:@"Type %@ is not allowed in JSON.", NSStringFromClass(jsonValueClass)];
JSONModelError* dataErr = [JSONModelError errorInvalidDataWithMessage:msg];
*err = [dataErr errorByPrependingKeyPathComponent:property.name];
}
return NO;
}
//check if there's matching property in the model
if (property) {
// check for custom setter, than the model doesn't need to do any guessing
// how to read the property's value from JSON
//setter方法进行赋值
if ([self __customSetValue:jsonValue forProperty:property]) {
//skip to next JSON key
continue;
};
// 0) handle primitives
// 基础类型,int,double等,直接使用keyPath赋值
if (property.type == nil && property.structName==nil) {
//generic setter
if (jsonValue != [self valueForKey:property.name]) {
[self setValue:jsonValue forKey: property.name];
}
//skip directly to the next key
continue;
}
// 0.5) handle nils
if (isNull(jsonValue)) {
if ([self valueForKey:property.name] != nil) {
[self setValue:nil forKey: property.name];
}
continue;
}
// 1) check if property is itself a JSONModel
//判断属性是否是JSONModel,如果是进行遍历之后根据keyPath赋值
if ([self __isJSONModelSubClass:property.type]) {
//initialize the property's model, store it
JSONModelError* initErr = nil;
id value = [[property.type alloc] initWithDictionary: jsonValue error:&initErr];
if (!value) {
//skip this property, continue with next property
if (property.isOptional || !validation) continue;
// Propagate the error, including the property name as the key-path component
if((err != nil) && (initErr != nil))
{
*err = [initErr errorByPrependingKeyPathComponent:property.name];
}
return NO;
}
if (![value isEqual:[self valueForKey:property.name]]) {
[self setValue:value forKey: property.name];
}
//for clarity, does the same without continue
continue;
} else {
// 2) check if there's a protocol to the property
// ) might or not be the case there's a built in transform for it
//判断是否包含protocol,用来表示字典或者数组中的数据类型,其中遍历后赋值
if (property.protocol) {
//JMLog(@"proto: %@", p.protocol);
jsonValue = [self __transform:jsonValue forProperty:property error:err];
if (!jsonValue) {
if ((err != nil) && (*err == nil)) {
NSString* msg = [NSString stringWithFormat:@"Failed to transform value, but no error was set during transformation. (%@)", property];
JSONModelError* dataErr = [JSONModelError errorInvalidDataWithMessage:msg];
*err = [dataErr errorByPrependingKeyPathComponent:property.name];
}
return NO;
}
}
// 3.1) handle matching standard JSON types
//处理匹配标准的json类型
if (property.isStandardJSONType && [jsonValue isKindOfClass: property.type]) {
//mutable properties
if (property.isMutable) {
jsonValue = [jsonValue mutableCopy];
}
//set the property value
if (![jsonValue isEqual:[self valueForKey:property.name]]) {
[self setValue:jsonValue forKey: property.name];
}
continue;
}
// 3.3) handle values to transform
//处理值的转换,如NSString转成NSNumber
if (
(![jsonValue isKindOfClass:property.type] && !isNull(jsonValue))
||
//the property is mutable
property.isMutable
||
//custom struct property
property.structName
) {
// searched around the web how to do this better
// but did not find any solution, maybe that's the best idea? (hardly)
//通过JSONValueTransformer转换
Class sourceClass = [JSONValueTransformer classByResolvingClusterClasses:[jsonValue class]];
//build a method selector for the property and json object classes
NSString* selectorName = [NSString stringWithFormat:@"%@From%@:",
(property.structName? property.structName : property.type), //target name
sourceClass]; //source name
SEL selector = NSSelectorFromString(selectorName);
//check for custom transformer
BOOL foundCustomTransformer = NO;
if ([valueTransformer respondsToSelector:selector]) {
foundCustomTransformer = YES;
} else {
//try for hidden custom transformer
selectorName = [NSString stringWithFormat:@"__%@",selectorName];
selector = NSSelectorFromString(selectorName);
if ([valueTransformer respondsToSelector:selector]) {
foundCustomTransformer = YES;
}
}
//check if there's a transformer with that name
if (foundCustomTransformer) {
//it's OK, believe me...
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
//transform the value
jsonValue = [valueTransformer performSelector:selector withObject:jsonValue];
#pragma clang diagnostic pop
if (![jsonValue isEqual:[self valueForKey:property.name]]) {
[self setValue:jsonValue forKey: property.name];
}
} else {
// it's not a JSON data type, and there's no transformer for it
// if property type is not supported - that's a programmer mistake -> exception
@throw [NSException exceptionWithName:@"Type not allowed"
reason:[NSString stringWithFormat:@"%@ type not supported for %@.%@", property.type, [self class], property.name]
userInfo:nil];
return NO;
}
} else {
// 3.4) handle "all other" cases (if any)
//处理其他case
if (![jsonValue isEqual:[self valueForKey:property.name]]) {
[self setValue:jsonValue forKey: property.name];
}
}
}
}
}
return YES;
}
整个func主要就做赋值工作
从对象的 <strong>properties
</strong> 数组中,循环到 <strong>dict
</strong> 中取值,通过 <strong>KVC
</strong> 操作。
循环中,使用 <strong>keyMapper
</strong> 的映射找对对应的字典属性的 <strong>keyPath
</strong> ,获取 <strong>jsonValue
</strong>
<pre>ps:因为是通过<strong>KVO</strong>赋值,根本是使用<strong>keyPath</strong>,所以<strong>keyMapper</strong>可以解决当<strong>model</strong>中属性名和<strong>json</strong>串中属性名不同的问题</pre>
赋值时分以下几种情况:
- 判断是不是空值,如果是空值并且属性非optional,error。
- 判断是不是合法的json类型,如果不是,error。
- 如果是属性:
3.1 如果有自定义setter调用自定义setter,然后continue。
3.2 如果没有自定setter时,属性是基本数据类型,int,double等,直接用KVC赋值。
3.3 如果没有自定setter时,属性是一个JSONModel,就解析这个JSONModel(递归),最终还是使用KVC赋值。
3.4 如果没有自定setter时,属性带有protocol字段,说明是个字典或者数组,将他遍历后解析。
3.5 如果没有自定setter时,属性和json串中字段均为标准json类型,直接用KVC赋值。
3.6 如果没有自定setter时,属性和json串中字段不同,使用JSONValueTransformer进行转换。
3.7 处理其他case,使用KVC直接赋值。
<pre>首先在OC中拥有很多簇类。
当我们debug的时候有时会发现一个NSString在底层不是NSString,有时是NSPlaceholderString,有时又是别的。
因为NSString在设计上得时候采用了抽象工厂的设计模式,内部是一个簇类(class cluster)。
这也是NSString,NSArray,NSDictionary什么的官方不建议去继承的原因。
使用JSONValueTransformer,就是由于这些簇类。需要使用JSONValueTransformer得到真正的类型,然后找到最适合的转换方法。
</pre>
总结
- 映射JSONModel的数据类型,<strong>
objc_getAssociatedObject
</strong> - 获取属性列表,<strong>
class_copyPropertyList
</strong> - 获得属性的描述,<strong>
property_getAttributes
</strong>,字符串类型 - KeyMapper映射
- Optional,Ignored等
- 底层KVC赋值,JSONValueTranformer完成类型转换。