JSONModel用法
根本用法
将传入的字典转换成模型:
起首定义模型类:
- @interface Person : JSONModel
- @property (nonatomic, copy) NSString *name;
- @property (nonatomic, copy) NSString *sex;
- @property (nonatomic, assign) NSInteger age;
- @end
复制代码 接着使用字典来转换为模型:
- NSDictionary *dict = @{
- @"name":@"Jack",
- @"age":@23,
- @"gender":@"male",
- };
- NSError *error;
- Person *person = [[Person alloc] initWithDictionary:dict error:&error];
复制代码 转换属性名称
有时间传入的字典的key名和J模型类的属性名称不匹配, 比如字典的key名被修改,从而导致非常,因此必要keyPapper方法来将模型中的属性转换成字典中对应的key名。
keyMapper方法必要返回一个字典,该字典的key是模型类的属性名称,value是传入的字典的key名
比如修改一下传入的字典里的gender字段为sex:
- @implementation Person
- + (JSONKeyMapper *)keyMapper
- {
- return [[JSONKeyMapper alloc] initWithModelToJSONDictionary:@{
- @"gender": @"sex", }];
- }
复制代码 这样一来,JSONKeyMapper就会自动帮我们做转换
自定义错误
JSONModel框架的作者答应开发者自定义错误阻止模型的转换
使用validate方法
比如实现当age对应的数值小于18等待时间输出未成年,并阻止模型的转换:
- - (BOOL)validate:(NSError **)error
- {
- if (![super validate:error])
- return NO;
-
- if (self.age < 18)
- {
- *error = [NSError errorWithDomain:@"未成年!" code:10 userInfo:nil];
- NSError *errorLog = *error;
- NSLog(@"%@",errorLog.domain);
- return NO;
- }
-
- return YES;
- }
复制代码 模型嵌套
源码分析
在JSONMoodel中提供了四种初始化方法:
- -(instancetype)initWithString:(NSString*)string error:(JSONModelError**)err;
- -(instancetype)initWithString:(NSString *)string usingEncoding:(NSStringEncoding)encoding error:(JSONModelError**)err;
- -(instancetype)initWithDictionary:(NSDictionary*)dict error:(NSError **)err;
- -(instancetype)initWithData:(NSData *)data error:(NSError **)error;
复制代码 这些初始化方法终极都会调用initWithDictionary方法
- -(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)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;
- }
复制代码 下面是initWithDictionary:的源码:
几个紧张的点:
- 关联对象kClassPropertiesKey
用来保存所有属性信息的NSDictionary)
- 关联对象kClassRequiredPropertyNamesKey
用来保存所有属性的名称的NSSet)
- 关联对象kMapperObjectKey
用来保存JSONKeyMapper):自定义的mapper,详细的方法就是用来自定义修改担当数据中的key
- JSONModelClassProperty:封装的jsonmodel的一个属性,它包含了对应属性的名字:(例如 name:gender),范例(例如 type:NSString),是否是JSONModel支持的范例(isStandardJSONType:YES/NO),是否是可变对象(isMutable:YES/NO)等属性。
整个执行流程: 起首,在这个模型类的对象被初始化的时间,遍历自身到所有的父类(直到JSONModel为止),获取所有的属性,并将其保存在一个字典里。获取传入字典的所有key,将这些key与保存的所有属性进行匹配。假如匹配成功,则进行kvc赋值。
initWithDictionary
- //这个方法里包含了作者做到的所有的容错和模型转化
- -(id)initWithDictionary:(NSDictionary*)dict error:(NSError**)err
- {
- //check for nil input
- //1.第一步判断传入的是否为nil
- if (!dict) {
- if (err) *err = [JSONModelError errorInputIsNil];
- return nil;
- }
- //invalid input, just create empty instance
- //2.第二步判断传入的是否为字典类型
- 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
- //3.创建类实例,通过init方法初始化映射property
- self = [self init];
- if (!self) {
- //super init didn't succeed
- if (err) *err = [JSONModelError errorModelIsInvalid];
- return nil;
- }
- //check incoming data structure
- //4.检查用户定义的模型里的属性集合是否大于传入的字典里的key集合(如果大于,则返回NO)就返回nil,并且抛出错误
- if (![self __doesDictionary:dict matchModelWithKeyMapper:self.__keyMapper error:err]) {
- return nil;
- }
- //import the data from a dictionary
- //5.根据传入的dict进行数据的赋值,如果赋值没有成功,就返回nil,并且抛出错误。
- if (![self __importDictionary:dict withKeyMapper:self.__keyMapper validation:YES error:err]) {
- return nil;
- }
- //run any custom model validation
- //6.根据本地的错误来判断是否有错误,如果有错误,就返回nil,并且抛出错误。
- if (![self validate:err]) {
- return nil;
- }
- //model is valid! yay!
- //7.前面的判断都通过,返回self
- return self;
- }
复制代码
- 判断传入的参数是否为空,假如为空直接返回nii
- 查抄参数是否是NSDictonary的实例,假如不是返回nil
- 初始化JSONModel实例,设置Model1的属性集合
- 查抄Model类的属性数量是否大于传入的的字典的key的数量,假如大于则返回NO
- 将传入的dict的值赋值给Model类的属性
- 假如重写了validate方法,则根据自定义的错误来阻拦model的返回
init
- - (id)init
- {
- self = [super init];
- if (self) {
- //do initial class setup
- [self __setup__];
- }
- return self;
- }
复制代码 在该方法中调用了setup方法
setup
- - (void)__setup__
- {
- //if first instance of this model, generate the property list
- // 如果是该模型的第一个实例,则生成属性列表
- if (!objc_getAssociatedObject(self.class, &kClassPropertiesKey)) {
- [self __inspectProperties];
- }
- //if there's a custom key mapper, store it in the associated object
- id mapper = [[self class] keyMapper];
- if ( mapper && !objc_getAssociatedObject(self.class, &kMapperObjectKey) ) {
- objc_setAssociatedObject(
- self.class,
- &kMapperObjectKey,
- mapper,
- OBJC_ASSOCIATION_RETAIN // This is atomic
- );
- }
- }
复制代码
- 起首通过objc_getAssociatedObject方法判断属性是否已经被缓存过,假如没有就调用inspectProperties方法将属性进行缓存
- 接着判断是否存在keyMapper方法,假如有就将keyMapper与模型类进行关联
__inspectProperties
- -(void)__inspectProperties
- {
- // 最终保存所有属性的字典,形式为:
- // {
- // age = "@property primitive age (Setters = [])";
- // friends = "@property NSArray* friends (Standard JSON type, Setters = [])";
- // gender = "@property NSString* gender (Standard JSON type, Setters = [])";
- // name = "@property NSString* name (Standard JSON type, Setters = [])";
- // }
- NSMutableDictionary* propertyIndex = [NSMutableDictionary dictionary];
- //获取当前的类名
- Class class = [self class];
- NSScanner* scanner = nil;
- NSString* propertyType = nil;
- // 循环条件:当class 是 JSONModel自己的时候终止
- while (class != [JSONModel class]) {
- //属性的个数
- unsigned int propertyCount;
- //获得属性列表(所有@property声明的属性)
- objc_property_t *properties = class_copyPropertyList(class, &propertyCount);
- //遍历所有的属性
- for (unsigned int i = 0; i < propertyCount; i++) {
- //获得属性名称
- objc_property_t property = properties[i];//获得当前的属性
- const char *propertyName = property_getName(property);//name(C字符串)
- //JSONModel里的每一个属性,都被封装成一个JSONModelClassProperty对象
- JSONModelClassProperty* p = [[JSONModelClassProperty alloc] init];
- p.name = @(propertyName);//propertyName:属性名称,例如:name,age,gender
- //获得属性类型
- const char *attrs = property_getAttributes(property);
- NSString* propertyAttributes = @(attrs);
- // T@"NSString",C,N,V_name
- // Tq,N,V_age
- // T@"NSString",C,N,V_gender
- // T@"NSArray",&,N,V_friends
- NSArray* attributeItems = [propertyAttributes componentsSeparatedByString:@","];
- //说明是只读属性,不做任何操作
- if ([attributeItems containsObject:@"R"]) {
- continue; //to next property
- }
- //检查出是布尔值
- if ([propertyAttributes hasPrefix:@"Tc,"]) {
- p.structName = @"BOOL";//使其变为结构体
- }
- //实例化一个scanner
- scanner = [NSScanner scannerWithString: propertyAttributes];
- [scanner scanUpToString:@"T" intoString: nil];
- [scanner scanString:@"T" intoString:nil];
- //http://blog.csdn.net/kmyhy/article/details/8258858
- if ([scanner scanString:@"@"" intoString: &propertyType]) {
- //属性是一个对象
- [scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@""<"]
- intoString:&propertyType];//propertyType -> NSString
- p.type = NSClassFromString(propertyType);// p.type = @"NSString"
- p.isMutable = ([propertyType rangeOfString:@"Mutable"].location != NSNotFound); //判断是否是可变的对象
- p.isStandardJSONType = [allowedJSONTypes containsObject:p.type];//是否是该框架兼容的类型
- //存在协议(数组,也就是嵌套模型)
- 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])
- //属性是结构体
- [scanner scanCharactersFromSet:[NSCharacterSet alphanumericCharacterSet]
- intoString:&propertyType];
- p.isStandardJSONType = NO;
- p.structName = propertyType;
- }
- else {
- //属性是基本类型:Tq,N,V_age
- [scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@","]
- intoString:&propertyType];
- //propertyType:q
- propertyType = valueTransformer.primitivesNames[propertyType];
- //propertyType:long
- //基本类型数组
- if (![allowedPrimitiveTypes containsObject:propertyType]) {
- //类型不支持
- @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);
- }
- //忽略block
- if ([propertyType isEqualToString:@"Block"]) {
- p = nil;
- }
- //如果字典里不存在,则添加到属性字典里(终于添加上去了。。。)
- if (p && ![propertyIndex objectForKey:p.name]) {
- [propertyIndex setValue:p forKey:p.name];
- }
- //setter 和 getter
- if (p)
- { //name ->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);
- //再指向自己的父类,知道等于JSONModel才停止
- class = [class superclass];
- }
- //最后保存所有当前类,JSONModel的所有的父类的属性
- objc_setAssociatedObject(
- self.class,
- &kClassPropertiesKey,
- [propertyIndex copy],
- OBJC_ASSOCIATION_RETAIN
- );
- }
复制代码
- 起首进入循环,循环终止的条件是当前类是JSONModel类。
- 接着使用 class_copyPropertyList方法获取Model类的属性列表
- 然后为每个属性创建一个JSONModelProperty对象
- JSONModelProperty对象是创建的propertyIndex的value值
- JSONModelProperty 对象中包含了属性名、数据范例、对应的 JSON 字段名等信息
- 将当前类的指针指向其父类
- 最后所有的这些 JSONModelProperty 对象都会存储在一个NSMutableDictionary 对象——propertyIndex中,然后通过objc_setAssociatedObject与模型进行关联
这个方法用于检索JSONModel类中的属性,并将其转化为一个可用的 NSDictionary 对象。该方法会遍历模型类的属性,然后解析每个属性的相干信息(如属性名、数据范例、对应的 JSON 字段名等),并将其存储在 NSDictionary 对象中,也就是上文的propertyIndex
- (BOOL)__doesDictionary NSDictionary*)dict matchModelWithKeyMapper JSONKeyMapper*)keyMapper
- //model类里面定义的属性集合是不能大于传入的字典里的key集合的。
- //如果存在了用户自定义的mapper,则需要按照用户的定义来进行转换。
- //(例如将gender转换为了sex)。
- -(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;
- //从array拿到set
- NSSet* incomingKeys = [NSSet setWithArray: incomingKeysArray];
- //transform the key names, if necessary
- //如有必要,变换键名称
- //如果用户自定义了mapper,则进行转换
- if (keyMapper || globalKeyMapper) {
- NSMutableSet* transformedIncomingKeys = [NSMutableSet setWithCapacity: requiredProperties.count];
- NSString* transformedName = nil;
- //loop over the required properties list
- //在所需属性列表上循环
- //遍历需要转换的属性列表
- for (JSONModelClassProperty* property in [self __properties__]) {
- //被转换成的属性名称(例如)TestModel(模型内) -> url(字典内)
- transformedName = (keyMapper||globalKeyMapper) ? [self __mapString:property.name withKeyMapper:keyMapper] : property.name;
- //check if exists and if so, add to incoming keys
- //检查是否存在,如果存在,则添加到传入密钥
- //(例如)拿到url以后,查看传入的字典里是否有url对应的值
- 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
- //检查是否缺少输入键
- //查看当前的model的属性的集合是否大于传入的属性集合,如果是,则返回错误
- //也就是说模型类里的属性是不能多于传入字典里的key的,例如:
- if (![requiredProperties isSubsetOfSet:incomingKeys]) {
- //get a list of the missing properties
- //获取缺失属性的列表(获取多出来的属性)
- [requiredProperties minusSet:incomingKeys];
- //not all required properties are in - invalid input
- //并非所有必需的属性都在 in - 输入无效
- 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;
- }
复制代码
- 起首获取dict中所有的key名传入数组incomingKeysArray中
- 接着获取Model类中所有的属性名传入集合equiredProperties中
- 将dict中得到的key数组转换为集合范例
- 假如存在keyMapper大概globalKeyMapper则将模型中的属性名转换为KeyMapper中对应的Value,也就是将Model类中的属性名转换为Json数据key名
- 更新dict的键名集合,这样做是为了包管dict中的每个键名都有对应的有效值,而不是仅仅只有一个key键
- 查抄Model类的属性数量是否大于传入的的字典的key的数量,假如大于则返回NO,因为此时JSON中的数据不能完全覆盖我们声明的属性
- (BOOL)__importDictionary NSDictionary*)dict withKeyMapper JSONKeyMapper*)keyMapper validation BOOL)validation error NSError**)err
使用kvc根据传入的dict进行数据的赋值,假如赋值没有成功,就返回nil,而且抛堕落误。
- //作者在最后给属性赋值的时候使用的是kvc的setValue:ForKey:的方法。
- //作者判断了模型里的属性的类型是否是JSONModel的子类,可见作者的考虑是非常周全的。
- //整个框架看下来,有很多的地方涉及到了错误判断,作者将将错误类型单独抽出一个类(JSONModelError),里面支持的错误类型很多,可以侧面反应作者思维之缜密。而且这个做法也可以在我们写自己的框架或者项目中使用。
- //从字典里获取值并赋给当前模型对象
- -(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
- //将属性的名称(若有改动就拿改后的名称)拿过来,作为key,用这个key来查找传进来的字典里对应的值
- 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
- //检查可选属性
- //字典不存在对应的key
- if (isNull(jsonValue)) {
- //skip this property, continue with next property
- //跳过此属性,继续下一个属性
- //如果这个key是可以不存在的
- if (property.isOptional || !validation) continue;
- //如果这个key是必须有的,则返回错误
- if (err) {
- //null value for required property
- //所需属性的值为null
- 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;
- }
- }
-
- //如果不兼容,则返回NO,mapping失败,抛出错误
- 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,则模型不需要进行任何猜测(查看是否有自定义setter,并设置)
- //如何从JSON读取属性值
- if ([self __customSetValue:jsonValue forProperty:property]) {
- //skip to next JSON key
- //跳到下一个JSON键
- continue;
- };
- // 0) handle primitives
- if (property.type == nil && property.structName==nil) {
- //generic setter
- //通用setter
- //kvc赋值
- 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类型
- 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;
- }
- //当前的属性值与value不同时,则赋值
- 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
- //2)检查是否有协议
- //)可能是,也可能不是,它有一个内置的转换
- if (property.protocol) {
- //JMLog(@"proto: %@", p.protocol);
- //转化为数组,这个数组就是例子中的friends属性
- 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
- //3.1)句柄匹配标准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
- //3.3)处理要转换的值
- //当前的值的类型与对应的属性的类型不一样的时候,需要查看用户是否自定义了转换器(例如从NSSet到NSArray转换:-(NSSet *)NSSetFromNSArray:(NSArray *)array)
-
- 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)
- //在网上搜索如何更好地做到这一点
- //但是没有找到任何解决方案,也许这是最好的主意?(几乎没有)
- 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
- //为属性和json对象类构建方法选择器
- 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) {
- IMP imp = [valueTransformer methodForSelector:selector];
- id (*func)(id, SEL, id) = (void *)imp;
- jsonValue = func(valueTransformer, selector, jsonValue);
- if (![jsonValue isEqual:[self valueForKey:property.name]])
- [self setValue:jsonValue forKey:property.name];
- } else {
- //如果没有自定义转换器,返回错误
- if (err) {
- 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)
- // 3.4) handle "all other" cases (if any)
- //3.4)处理“所有其他”情况(如有)
- if (![jsonValue isEqual:[self valueForKey:property.name]])
- [self setValue:jsonValue forKey:property.name];
- }
- }
- }
- }
- return YES;
- }
复制代码
- 起首遍历模型类中的每个属性
- 接着从JSON数据中拿出真正对应property的value,进行value一系列的值判断
- value可用的情况下,就开始进行赋值,有setter方法的通过setter方法赋值,底子范例int,float等直接赋值
- 假如property又是一个JSONModel,就递归先将子Model进行整体解析。
- 假如包含protocol字段,则表明内部是一个array大概dictionary,并包含这个protocol字段的对象解析。
- 对于其他情况,应该是一种范例的转换,通过获取值范例和property范例,调用相应的转换方法进行赋值。
- 作者在最后给属性赋值的时间使用的是kvc的setValue:ForKey:的方法。
- 作者判断了模型里的属性的范例是否是JSONModel的子类
- 整个框架看下来,有很多的地方涉及到了错误判断,作者将将错误范例单独抽出一个类(JSONModelError),内里支持的错误范例很多,体现了作者头脑的缜密。而且这个做法也可以在我们写自己的框架大概项目中使用
总结
通过获取Model类的属性列表,与传入的JSON数据自动匹配,同时还可以通过KeyMapper修改不相同的映射,假如模型类与JSON数据字段不匹配则会抛堕落误(这里体现为Model中某些必须的属性没有在JSON数据中找到相应的映射),最后假如范例等都查抄成功,则通过KVC将JSON数据中的value设置在Model类的对应的属性上
- Runtime中动态解析Model数据范例,可以实现自动匹配
- 已经解析过的Model1的属性列表会通过AssociatedObject进行缓存制止重复解析
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |