1 使用方法
对JSONModel 做一个简单的封装
定义一个基础模型DMBaseModel继承自JSONModel,对初始化方法做一个简单的封装,以及将属性设置为可选(Optional)
DMBaseModel.h 文件如下
@interface DMBaseModel : JSONModel
- (instancetype)initWithDictionary:(NSDictionary *)dict;
- (instancetype)initWithData:(NSData *)data;
- (instancetype)initWithString:(NSString *)string;
@end
DMBaseModel.m文件如下
#import "DMBaseModel.h"
@implementation DMBaseModel
+ (BOOL)propertyIsOptional:(NSString *)propertyName
{
return YES;
}
- (instancetype)initWithDictionary:(NSDictionary *)dict
{
return [self initWithDictionary:dict error:nil];
}
- (instancetype)initWithData:(NSData *)data
{
return [self initWithData:data error:nil];
}
- (instancetype)initWithString:(NSString *)string
{
return [self initWithString:string error:nil];
}
@end
定义一个模型继承自DMBaseModel
DMProductModel.h文件如下
#import "DMBaseModel.h"
@protocol DMProductModel
@end
@interface DMProductModel : DMBaseModel
/** 商品Id */
@property (nonatomic, strong) NSString *productId;
/** 商品名称 */
@property (nonatomic, strong) NSString *productName;
/** 商品售价 */
@property (nonatomic, strong) NSNumber *productSalePrice;
/** 测试结构体类型 */
@property (nonatomic, assign) CGPoint productPoint;
@end
DMProductModel.m文件如下,定义了转换属性名称
#import "DMProductModel.h"
@implementation DMProductModel
+ (JSONKeyMapper *)keyMapper
{
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
/** params字典中setObject:forKey:
* object值为JSON中的key值
* key值为模型中的属性值
*/
[params setObject:@"salePrice" forKey:@"productSalePrice"];
return [[JSONKeyMapper alloc] initWithModelToJSONDictionary:params];
}
/** JSON解析时忽略的属性 */
+ (BOOL)propertyIsIgnored:(NSString *)propertyName
{
if ([propertyName isEqualToString:@"productPoint"]) {
return YES;
}
return NO;
}
@end
字典中的key值为salePrice时,也可以转换如下
NSMutableDictionary *productDict = [[NSMutableDictionary alloc] init];
[productDict setObject:[NSNumber numberWithInteger:1000001] forKey:@"productId"];
[productDict setObject:@"卡地亚山度士自动机械腕表" forKey:@"productName"];
[productDict setObject:[NSNumber numberWithDouble:36999.99] forKey:@"salePrice"];
/*
CGPoint tempPoint = CGPointMake(100, 100);
[productDict setObject:[NSValue valueWithCGPoint:tempPoint] forKey:@"productPoint"];
// JSONModel 不能解析NSValue,因为NSValue不是JSON允许的格式
*/
DMProductModel *productOne = [[DMProductModel alloc] initWithDictionary:productDict];
模型嵌套时
#import "DMBaseModel.h"
#import "DMProductModel.h"
#import "DMImageInfoModel.h"
@interface DMUserModel : DMBaseModel
/** 用户Id */
@property (nonatomic, strong) NSString *userId;
/** 用户名称 */
@property (nonatomic, strong) NSString *userName;
/** 用户年龄 */
@property (nonatomic, assign) NSInteger userAge;
/** 喜欢商品数组 */
@property (nonatomic, strong) NSArray<DMProductModel> *loveProductsArray;
/** 用户头像 */
@property (nonatomic, strong) DMImageInfoModel *headIcon;
@end
对DMProductModel 模型嵌套,需要声明 DMProductModel协议,参考DMProductModel.h
@protocol DMProductModel
@end
转化如下
NSMutableDictionary *userDict = [[NSMutableDictionary alloc] init];
[userDict setObject:@"DM000001" forKey:@"userId"];
[userDict setObject:@"JackZ86" forKey:@"userName"];
[userDict setObject:[NSNumber numberWithInteger:18] forKey:@"userAge"];
NSMutableArray *productArray = [[NSMutableArray alloc] init];
{
NSMutableDictionary *productDict = [[NSMutableDictionary alloc] init];
[productDict setObject:@"000001" forKey:@"productId"];
[productDict setObject:@"卡地亚山度士自动机械腕表" forKey:@"productName"];
[productDict setObject:[NSNumber numberWithDouble:36999.99] forKey:@"salePrice"];
[productArray addObject:productDict];
}
{
NSMutableDictionary *productDict = [[NSMutableDictionary alloc] init];
[productDict setObject:@"000002" forKey:@"productId"];
[productDict setObject:@"LV手提包" forKey:@"productName"];
[productDict setObject:[NSNumber numberWithDouble:66.66] forKey:@"salePrice"];
[productArray addObject:productDict];
}
{
NSMutableDictionary *productDict = [[NSMutableDictionary alloc] init];
[productDict setObject:[NSNumber numberWithInteger:100003] forKey:@"productId"];
[productDict setObject:@"匡威经典鞋" forKey:@"productName"];
[productDict setObject:[NSNumber numberWithDouble:888.88] forKey:@"salePrice"];
[productArray addObject:productDict];
}
[userDict setObject:productArray forKey:@"loveProductsArray"];
{
NSMutableDictionary *imgInfoDict = [[NSMutableDictionary alloc] init];
[imgInfoDict setObject:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1531977670688&di=e6b8cbb146159c0ac82ccb52013ada79&imgtype=0&src=http%3A%2F%2Fimg4.duitang.com%2Fuploads%2Fblog%2F201309%2F15%2F20130915195908_UurWc.thumb.700_0.jpeg" forKey:@"imgUrl"];
[imgInfoDict setObject:[NSNumber numberWithInteger:375] forKey:@"imgWidth"];
[imgInfoDict setObject:[NSNumber numberWithInteger:200] forKey:@"imgHeight"];
[userDict setObject:imgInfoDict forKey:@"headIcon"];
}
DMUserModel *userModel = [[DMUserModel alloc] initWithDictionary:userDict];
2 源码解析
初始化方法
- (instancetype)initWithString:(NSString *)string error:(JSONModelError **)err;
- (instancetype)initWithString:(NSString *)string usingEncoding:(NSStringEncoding)encoding error:(JSONModelError **)err;
- (instancetype)initWithData:(NSData *)data error:(NSError **)error;
- (instancetype)initWithDictionary:(NSDictionary *)dict error:(NSError **)err;
前三种方法实现
- (id)initWithString:(NSString *)string error:(JSONModelError **)err
{
JSONModelError *initError = nil;
id objModel = [self initWithString:string usingEncoding:NSUTF8StringEncoding error:&initError];
if (initError && err) {
*err = initError;
}
return objModel;
}
- (id)initWithString:(NSString *)string usingEncoding:(NSStringEncoding)encoding error:(JSONModelError **)err
{
//check for nil input
if (!string) {
if (err) {
*err = [JSONModelError errorInputIsNil];
}
return nil;
}
JSONModelError *initError = nil;
id objModel = [self initWithData:[string dataUsingEncoding:encoding] error:&initError];
if (initError && err) {
*err = initError;
}
return objModel;
}
- (instancetype)initWithData:(NSData *)data error:(NSError *__autoreleasing *)err
{
//check for nil input
if (!data) {
if (err) {
*err = [JSONModelError errorInputIsNil];
}
return nil;
}
//read the json
JSONModelError *initError = nil;
id obj = [NSJSONSerialization JSONObjectWithData:data
options:kNilOptions
error:&initError];
if (initError) {
if (err) {
*err = [JSONModelError errorBadJSON];
}
return nil;
}
//init with dictionary
id objModel = [self initWithDictionary:obj error:&initError];
if (initError && err) {
*err = initError;
}
return objModel;
}
通过代码可以知道最终都会调用到- (id)initWithDictionary:(NSDictionary *)dict error:(NSError **)err初始化方法
看- (id)initWithDictionary:(NSDictionary *)dict error:(NSError **)err方法之前先看一下+ (void)load方法
2.1 load方法源码
+ (void)load
{
static dispatch_once_t once;
dispatch_once(&once, ^{
// initialize all class static objects,
// which are common for ALL JSONModel subclasses
@autoreleasepool {
// 兼容的对象类型
allowedJSONTypes = @[
[NSString class],
[NSNumber class],
[NSDecimalNumber class],
[NSArray class],
[NSDictionary class],
[NSNull class], //immutable JSON classes
[NSMutableString class],
[NSMutableArray class],
[NSMutableDictionary class] //mutable JSON classes
];
// 兼容的基本类型
allowedPrimitiveTypes = @[
@"BOOL",
@"float",
@"int",
@"long",
@"double",
@"short",
//and some famous aliases
@"NSInteger",
@"NSUInteger",
@"Block"
];
// 转换器
valueTransformer = [[JSONValueTransformer alloc] init];
// This is quite strange, but I found the test isSubclassOfClass: (line ~291) to fail if using [JSONModel class].
// somewhat related: https://stackoverflow.com/questions/6524165/nsclassfromstring-vs-classnamednsstring
// //; seems to break the unit tests
// Using NSClassFromString instead of [JSONModel class], as this was breaking unit tests, see below
//http://stackoverflow.com/questions/21394919/xcode-5-unit-test-seeing-wrong-class
// 类型
JSONModelClass = NSClassFromString(NSStringFromClass(self));
}
});
}
这个方法主要是对静态变量的赋值,比如兼容的对象类型、兼容的基本类型、转换器、以及Class类型
2.2 初始化- (id)initWithDictionary:(NSDictionary *)dict error:(NSError **)方法源码
- (id)initWithDictionary:(NSDictionary *)dict error:(NSError **)err
{
//check for nil input
// 输入dict是否为空
if (!dict) {
if (err) {
*err = [JSONModelError errorInputIsNil];
}
return nil;
}
//invalid input, just create empty instance
// 输入dict是否是字典类型
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
// 通过dict对属性进行赋值
if (![self __importDictionary:dict withKeyMapper:self.__keyMapper validation:YES error:err]) {
return nil;
}
//run any custom model validation
// 自定义model的验证
if (![self validate:err]) {
return nil;
}
//model is valid! yay!
return self;
}
首先是对输入dict是否为nil,以及是否是字典的检查,然后是对model的初始化,检查输入数据的完整性,再然后才是对属性的赋值,最后对model的验证,返回
2.2.1 init方法源码
- (id)init
{
self = [super init];
if (self) {
//do initial class setup
[self __setup__];
}
return self;
}
- (void)__setup__
{
//if first instance of this model, generate the property list
// 获取关联kClassPropertiesKey属性值
if (!objc_getAssociatedObject(self.class, &kClassPropertiesKey)) {
[self __inspectProperties];
}
//if there's a custom key mapper, store it in the associated object
// 如果自定义了keyMapper; 将其保存在关联对象kMapperObjectKey中
id mapper = [[self class] keyMapper];
if ( mapper && !objc_getAssociatedObject(self.class, &kMapperObjectKey) ) {
objc_setAssociatedObject(
self.class,
&kMapperObjectKey,
mapper,
OBJC_ASSOCIATION_RETAIN // This is atomic
);
}
}
关联kClassPropertiesKey属性值以及关联kMapperObjectKey属性值
2.2.1.1 __inspectProperties方法源码
- (void)__inspectProperties
{
//JMLog(@"Inspect class: %@", [self class]);
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
// 循环条件: 当class 类名是JSONModel 时候跳出循环
while (class != [JSONModel class]) {
//JMLog(@"inspecting: %@", NSStringFromClass(class));
// 属性个数
unsigned int propertyCount;
// 获取属性列表 (所有@property 声明的属性)
objc_property_t *properties = class_copyPropertyList(class, &propertyCount);
//loop over the class properties
// 遍历所有的属性
for (unsigned int i = 0; i < propertyCount; i++) {
// 每一个属性都封装成JSONModelClassProperty 对象
JSONModelClassProperty *p = [[JSONModelClassProperty alloc] init];
//get property name
// 获取当前属性
objc_property_t property = properties[i];
// 获取属性名
const char *propertyName = property_getName(property);
p.name = @(propertyName);
//JMLog(@"property: %@", p.name);
//get property attributes
// 获取属性类型
const char *attrs = property_getAttributes(property);
NSString *propertyAttributes = @(attrs);
/** DMProductModel 属性类型 */
// T@"NSString",&,N,V_productId
// T@"NSString",&,N,V_productName
// T@"NSNumber",&,N,V_productSalePrice
// T{CGPoint=dd},N,V_productPoint
/** DMProductModel 属性类型 */
/** DMUserModel 属性类型 */
// T@"NSString",&,N,V_userId
// T@"NSString",&,N,V_userName
// Tq,N,V_userAge
// T@"NSArray<DMProductModel>",&,N,V_loveProductsArray
// T@"DMImageInfoModel",&,N,V_headIcon
/** DMUserModel 属性类型 */
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";
}
scanner = [NSScanner scannerWithString: propertyAttributes];
//JMLog(@"attr: %@", [NSString stringWithCString:attrs encoding:NSUTF8StringEncoding]);
[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];
//JMLog(@"type: %@", propertyClassName);
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];
}
} else if ([scanner scanString:@"{" intoString: &propertyType]) {
//check if the property is a structure
/**
* 属性是结构体类型
* T{CGPoint=dd},N,V_productPoint 时,structName为CGPoint
*/
[scanner scanCharactersFromSet:[NSCharacterSet alphanumericCharacterSet]
intoString:&propertyType];
p.isStandardJSONType = NO;
p.structName = propertyType;
} else {
//the property must be a primitive
// 属性是基本类型
//the property contains a primitive data type
[scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@","]
intoString:&propertyType];
//get the full name of the primitive type
/**
* // Tq,N,V_userAge 时 propertyType 时q
* 通过转换器中的基本类型字符映射获取基本类型的全名
*/
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];
}
}
NSString *nsPropertyName = @(propertyName);
// 属性是否可选
if([[self class] propertyIsOptional:nsPropertyName]){
p.isOptional = YES;
}
// 属性是否可以忽略
if([[self class] propertyIsIgnored:nsPropertyName]){
p = nil;
}
// 属性是集合类型,是否遵守协议
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
// p不为nil并且字典中不存在p.name对应的值时,将p加入字典
if (p && ![propertyIndex objectForKey:p.name]) {
[propertyIndex setValue:p forKey:p.name];
}
// generate custom setters and getter
// 是否有自定义的getter and setter
if (p) {
// 将p.name首字母大写
NSString *name = [p.name stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:[p.name substringToIndex:1].uppercaseString];
// getter
SEL getter = NSSelectorFromString([NSString stringWithFormat:@"JSONObjectFor%@", name]);
if ([self respondsToSelector:getter]) {
p.customGetter = getter;
}
// setters
p.customSetters = [NSMutableDictionary new];
SEL genericSetter = NSSelectorFromString([NSString stringWithFormat:@"set%@WithJSONObject:", name]);
if ([self respondsToSelector:genericSetter]) {
p.customSetters[@"generic"] = [NSValue valueWithBytes:&genericSetter objCType:@encode(SEL)];
}
for (Class type in allowedJSONTypes) {
NSString *class = NSStringFromClass([JSONValueTransformer classByResolvingClusterClasses:type]);
if (p.customSetters[class])
continue;
SEL setter = NSSelectorFromString([NSString stringWithFormat:@"set%@With%@:", name, class]);
if ([self respondsToSelector:setter])
p.customSetters[class] = [NSValue valueWithBytes:&setter objCType:@encode(SEL)];
}
}
}
free(properties);
//ascend to the super of the class
//(will do that until it reaches the root class - JSONModel)
// 将class 指向父类的类型
class = [class superclass];
}
//finally store the property index in the static property index
// 将获取的属性字典保存到kClassPropertiesKey关联属性中
objc_setAssociatedObject(
self.class,
&kClassPropertiesKey,
[propertyIndex copy],
OBJC_ASSOCIATION_RETAIN // This is atomic
);
}
获取当前类直到父类为JSONModel时的所有属性,并将每一个属性包装到JSONModelClassProperty对象中,最后将获取的属性字典保存到kClassPropertiesKey关联属性中
关于属性的type encodings
/** DMUserModel 属性类型 */
// T@"NSString",&,N,V_userId
// T@"NSString",&,N,V_userName
// Tq,N,V_userAge
// T@"NSArray<DMProductModel>",&,N,V_loveProductsArray
// T@"DMImageInfoModel",&,N,V_headIcon
/** DMUserModel 属性类型 */
像Tq,N,V_userAge这种时,会通过valueTransformer转换判断基本类型是否在兼容的基本类型中, valueTransformer转换的源码
- (id)init
{
self = [super init];
if (self) {
_primitivesNames = @{@"f":@"float",
@"i":@"int",
@"d":@"double",
@"l":@"long",
@"c":@"BOOL",
@"s":@"short",
@"q":@"long",
//and some famous aliases of primitive types
// BOOL is now "B" on iOS __LP64 builds
@"I":@"NSInteger",
@"Q":@"NSUInteger",
@"B":@"BOOL",
@"@?":@"Block"};
}
return self;
}
2.2.2 - (BOOL)__doesDictionary:(NSDictionary *)dict matchModelWithKeyMapper:(JSONKeyMapper *)keyMapper error:(NSError **)err方法的源码
/**
* 验证输入字典dict中是否包含所有的必选属性值
*/
- (BOOL)__doesDictionary:(NSDictionary *)dict matchModelWithKeyMapper:(JSONKeyMapper *)keyMapper error:(NSError **)err
{
//check if all required properties are present
// 获取字典中所有的key值
NSArray* incomingKeysArray = [dict allKeys];
// 获取所有的必选属性
NSMutableSet* requiredProperties = [self __requiredPropertyNames].mutableCopy;
NSSet* incomingKeys = [NSSet setWithArray: incomingKeysArray];
//transform the key names, if necessary
// 存在自定义的keyMapper, 转换key名
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
// 字典dict中是否存在键对应的值
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
// 必选属性集合是否是incomingKeys集合的子集
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;
}
验证传入的字典中存在所有model必选属性对应的值
2.2.2.1 如果存在自定义的keyMapper,获取转换之后的键值
/**
* 获取转化之后的键值
*/
- (NSString *)__mapString:(NSString *)string withKeyMapper:(JSONKeyMapper *)keyMapper
{
if (keyMapper) {
//custom mapper
// 存在自定义的JSONKeyMapper, 获取属性名对应到json中的key值
NSString* mappedName = [keyMapper convertValue:string];
if (globalKeyMapper && [mappedName isEqualToString: string]) {
mappedName = [globalKeyMapper convertValue:string];
}
string = mappedName;
} else if (globalKeyMapper) {
//global keymapper
string = [globalKeyMapper convertValue:string];
}
return string;
}
2.2.3 通过字典赋值方法源码
- (BOOL)__importDictionary:(NSDictionary *)dict withKeyMapper:(JSONKeyMapper *)keyMapper validation:(BOOL)validation error:(NSError **)err
{
//loop over the incoming keys and set self's properties
// 遍历属性集合
for (JSONModelClassProperty* property in [self __properties__]) {
//convert key name to model keys, if a mapper is provided
// 转化键值
NSString* jsonKeyPath = (keyMapper||globalKeyMapper) ? [self __mapString:property.name withKeyMapper:keyMapper] : property.name;
//JMLog(@"keyPath: %@", jsonKeyPath);
//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
// 存在自定义方法就直接调用自定义方法
if ([self __customSetValue:jsonValue forProperty:property]) {
//skip to next JSON key
continue;
};
// 0) handle primitives
if (property.type == nil && property.structName==nil) {
// 处理基本类型 ,如这个
// Tq,N,V_userAge
//generic setter
if (jsonValue != [self valueForKey:property.name]) {
// 通过KVC赋值
[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
if ([self __isJSONModelSubClass:property.type]) {
// 属性是JSONModel的子类,如下面
// T@"DMImageInfoModel",&,N,V_headIcon
//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]]) {
// 通过KVC赋值
[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
// 属性遵守协议
if (property.protocol) {
//JMLog(@"proto: %@", p.protocol);
// 转换jsonValue 根据property , 如下面的这个
// T@"NSArray<DMProductModel>",&,N,V_loveProductsArray
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
if (property.isStandardJSONType && [jsonValue isKindOfClass: property.type]) {
// 属性是标准类型 并且 jsonValue 也是属性type类型
//mutable properties
if (property.isMutable) {
jsonValue = [jsonValue mutableCopy];
}
//set the property value
if (![jsonValue isEqual:[self valueForKey:property.name]]) {
// 通过KVC赋值
[self setValue:jsonValue forKey: property.name];
}
continue;
}
// 3.3) handle values to transform
if (
(![jsonValue isKindOfClass:property.type] && !isNull(jsonValue))
||
//the property is mutable
property.isMutable
||
//custom struct property
property.structName
) {
// 处理 需要转化的,例如(字符串NSString productId 但是传入的字典中 [productDict setObject:[NSNumber numberWithInteger:1000001] forKey:@"productId"] 是一个NSNumber)
// searched around the web how to do this better
// but did not find any solution, maybe that's the best idea? (hardly)
Class sourceClass = [JSONValueTransformer classByResolvingClusterClasses:[jsonValue class]];
//JMLog(@"to type: [%@] from type: [%@] transformer: [%@]", p.type, sourceClass, selectorName);
//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
// 转换器valueTransformer中能否找到对应转换方法
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]]) {
// KVC赋值
[self setValue:jsonValue forKey: property.name];
}
} else {
NSString* msg = [NSString stringWithFormat:@"%@ type not supported for %@.%@", property.type, [self class], property.name];
JSONModelError* dataErr = [JSONModelError errorInvalidDataWithTypeMismatch:msg];
*err = [dataErr errorByPrependingKeyPathComponent:property.name];
return NO;
}
} else {
// 3.4) handle "all other" cases (if any)
// 其它情况
if (![jsonValue isEqual:[self valueForKey:property.name]]) {
[self setValue:jsonValue forKey: property.name];
}
}
}
}
}
return YES;
}
会遍历每一个属性列表,如果字典中存在对应的jsonValue值,会根据jsonValue值进行不同的处理
0)基本类型时通过KVC直接赋值,例如Tq,N,V_userAge
1)属性是JSONModel的子类时,会将jsonValue通过initWithDictionary方法转化为对象,然后通过KVC赋值,例如T@"DMImageInfoModel",&,N,V_headIcon
2)属性遵守协议时,会将jsonValue根据属性来转换,例如T@"NSArray<DMProductModel>",&,N,V_loveProductsArray
2.2.3.1 根据property转换jsonValue源码
/**
* 转换value 根据property
*/
- (id)__transform:(id)value forProperty:(JSONModelClassProperty *)property error:(NSError **)err
{
// 协议从NSString转化成类型
Class protocolClass = NSClassFromString(property.protocol);
if (!protocolClass) {
//no other protocols on arrays and dictionaries
//except JSONModel classes
if ([value isKindOfClass:[NSArray class]]) {
@throw [NSException exceptionWithName:@"Bad property protocol declaration"
reason:[NSString stringWithFormat:@"<%@> is not allowed JSONModel property protocol, and not a JSONModel class.", property.protocol]
userInfo:nil];
}
return value;
}
// 协议类型是JSONModel的子类
if ([self __isJSONModelSubClass:protocolClass]) {
//if the protocol is actually a JSONModel class
//check if it's a list of models
// 属性类型是NSArray的子类
if ([property.type isSubclassOfClass:[NSArray class]]) {
// Expecting an array, make sure 'value' is an array
// 检查value的类型是NSArray的子类
if(![[value class] isSubclassOfClass:[NSArray class]]) {
if(err != nil) {
NSString* mismatch = [NSString stringWithFormat:@"Property '%@' is declared as NSArray<%@>* but the corresponding JSON value is not a JSON Array.", property.name, property.protocol];
JSONModelError* typeErr = [JSONModelError errorInvalidDataWithTypeMismatch:mismatch];
*err = [typeErr errorByPrependingKeyPathComponent:property.name];
}
return nil;
}
//one shot conversion
JSONModelError* arrayErr = nil;
value = [[protocolClass class] arrayOfModelsFromDictionaries:value error:&arrayErr];
if((err != nil) && (arrayErr != nil)) {
*err = [arrayErr errorByPrependingKeyPathComponent:property.name];
return nil;
}
} else if ([property.type isSubclassOfClass:[NSDictionary class]]) {
//check if it's a dictionary of models
// 属性类型是NSDictionary的子类
// Expecting a dictionary, make sure 'value' is a dictionary
// 检查value的类型是NSDictionary的子类
if(![[value class] isSubclassOfClass:[NSDictionary class]]) {
if(err != nil) {
NSString* mismatch = [NSString stringWithFormat:@"Property '%@' is declared as NSDictionary<%@>* but the corresponding JSON value is not a JSON Object.", property.name, property.protocol];
JSONModelError* typeErr = [JSONModelError errorInvalidDataWithTypeMismatch:mismatch];
*err = [typeErr errorByPrependingKeyPathComponent:property.name];
}
return nil;
}
NSMutableDictionary* res = [NSMutableDictionary dictionary];
for (NSString* key in [value allKeys]) {
JSONModelError* initErr = nil;
id obj = [[[protocolClass class] alloc] initWithDictionary:value[key] error:&initErr];
if (obj == nil) {
// Propagate the error, including the property name as the key-path component
if((err != nil) && (initErr != nil)) {
initErr = [initErr errorByPrependingKeyPathComponent:key];
*err = [initErr errorByPrependingKeyPathComponent:property.name];
}
return nil;
}
[res setValue:obj forKey:key];
}
value = [NSDictionary dictionaryWithDictionary:res];
}
}
return value;
}
3.1)不是JsonModel 对象类型,也不是基本类型时,如果是兼容的标准类型并且jsonValue也是属性对应的类型时直接通过KVC赋值
3.3)处理需要转换的类型
需要特殊转换的类型,是通过valueTransformer来转换的,查找valueTransformer中是否存在对应的转换函数,例如字符串NSString productId 但是传入的字典中 [productDict setObject:[NSNumber numberWithInteger:1000001] forKey:@"productId"] 是一个NSNumber,就会找到valueTransformer的NSStringFromNSNumber函数来转换,转换完成之后通过KVC赋值
3.4) 其它情况
2.2.4 validate方法
- (BOOL)validate:(NSError **)error
{
return YES;
}
可以通过validate方法来处理model转换特殊错误处理
2.3 数组转化成model
+ (NSMutableArray *)arrayOfModelsFromDictionaries:(NSArray *)array error:(NSError **)err
{
//bail early
if (isNull(array))
return nil;
//parse dictionaries to objects
NSMutableArray* list = [NSMutableArray arrayWithCapacity: [array count]];
for (id d in array) {
if ([d isKindOfClass:NSDictionary.class]) {
JSONModelError* initErr = nil;
id obj = [[self alloc] initWithDictionary:d error:&initErr];
if (obj == nil) {
// Propagate the error, including the array index as the key-path component
if((err != nil) && (initErr != nil)) {
NSString* path = [NSString stringWithFormat:@"[%lu]", (unsigned long)list.count];
*err = [initErr errorByPrependingKeyPathComponent:path];
}
return nil;
}
[list addObject: obj];
} else if ([d isKindOfClass:NSArray.class]) {
[list addObjectsFromArray:[self arrayOfModelsFromDictionaries:d error:err]];
} else {
// This is very bad
}
}
return list;
}
2.4
- (NSString *)toJSONString;
- (NSData *)toJSONData;
- (NSDictionary *)toDictionary;
- (NSDictionary *)toDictionaryWithKeys:(NSArray *)propertyNames;
前三种方法都是通过- (NSDictionary *)toDictionaryWithKeys:(NSArray *)propertyNames方法实现的
//exports the model as a dictionary of JSON compliant objects
- (NSDictionary *)toDictionaryWithKeys:(NSArray *)propertyNames
{
NSArray* properties = [self __properties__];
NSMutableDictionary* tempDictionary = [NSMutableDictionary dictionaryWithCapacity:properties.count];
id value;
//loop over all properties
for (JSONModelClassProperty* p in properties) {
//skip if unwanted
if (propertyNames != nil && ![propertyNames containsObject:p.name])
continue;
//fetch key and value
NSString* keyPath = (self.__keyMapper||globalKeyMapper) ? [self __mapString:p.name withKeyMapper:self.__keyMapper] : p.name;
value = [self valueForKey: p.name];
//JMLog(@"toDictionary[%@]->[%@] = '%@'", p.name, keyPath, value);
if ([keyPath rangeOfString:@"."].location != NSNotFound) {
//there are sub-keys, introduce dictionaries for them
[self __createDictionariesForKeyPath:keyPath inDictionary:&tempDictionary];
}
//check for custom getter
if ([self __customGetValue:&value forProperty:p]) {
//custom getter, all done
[tempDictionary setValue:value forKeyPath:keyPath];
continue;
}
//export nil when they are not optional values as JSON null, so that the structure of the exported data
//is still valid if it's to be imported as a model again
if (isNull(value)) {
if (value == nil)
{
[tempDictionary removeObjectForKey:keyPath];
}
else
{
[tempDictionary setValue:[NSNull null] forKeyPath:keyPath];
}
continue;
}
//check if the property is another model
if ([value isKindOfClass:JSONModelClass]) {
//recurse models
value = [(JSONModel*)value toDictionary];
[tempDictionary setValue:value forKeyPath: keyPath];
//for clarity
continue;
} else {
// 1) check for built-in transformation
if (p.protocol) {
value = [self __reverseTransform:value forProperty:p];
}
// 2) check for standard types OR 2.1) primitives
if (p.structName==nil && (p.isStandardJSONType || p.type==nil)) {
//generic get value
[tempDictionary setValue:value forKeyPath: keyPath];
continue;
}
// 3) try to apply a value transformer
if (YES) {
//create selector from the property's class name
NSString* selectorName = [NSString stringWithFormat:@"%@From%@:", @"JSONObject", p.type?p.type:p.structName];
SEL selector = NSSelectorFromString(selectorName);
BOOL foundCustomTransformer = NO;
if ([valueTransformer respondsToSelector:selector]) {
foundCustomTransformer = YES;
} else {
//try for hidden transformer
selectorName = [NSString stringWithFormat:@"__%@",selectorName];
selector = NSSelectorFromString(selectorName);
if ([valueTransformer respondsToSelector:selector]) {
foundCustomTransformer = YES;
}
}
//check if there's a transformer declared
if (foundCustomTransformer) {
//it's OK, believe me...
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
value = [valueTransformer performSelector:selector withObject:value];
#pragma clang diagnostic pop
[tempDictionary setValue:value forKeyPath: keyPath];
} else {
//in this case most probably a custom property was defined in a model
//but no default reverse transformer for it
@throw [NSException exceptionWithName:@"Value transformer not found"
reason:[NSString stringWithFormat:@"[JSONValueTransformer %@] not found", selectorName]
userInfo:nil];
return nil;
}
}
}
}
return [tempDictionary copy];
}
理解了如何从json转化成Model,这个只是逆向的一个过程。
网友评论