iOS ------ JSONModel源码

一,JSONModel的基本使用

1,基本使用方法

- (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;

这四个方法都是 JSONModel 类中的初始化方法,用于创建并初始化 JSONModel 的实例对象。
使用情况:

  • initWithDictionary:error:
    将一个包含 JSON 对象的字典转换为 JSONModel 实例对象时使用。
  • initWithData:error:
    将从从网络获取到 JSON 数据转换为 JSONModel 实例对象时使用。
  • initWithString:error:
    将一个 JSON 字符串转换为 JSONModel 实例对象时使用。
  • initWithString:usingEncoding:error:
    当您已经有一个 JSON 字符串,并需要指定字符编码时使用。

这些方法提供了不同的输入方式,以适应不同的数据源和数据表示形式。

2,转换属性名称

有时我们获取的josn数据的key发生变化和定义的model对象的属性不一样,而模型属性不能轻易改变。比如原来字典里的gender这个key变成了sex,这就需要我们定义一个转换的mapper(JSONKeyMapper):

@implementation Person
+ (JSONKeyMapper *)keyMapper
{return [[JSONKeyMapper alloc] initWithModelToJSONDictionary:@{@"gender": @"sex",                                                             }];
}

具体效果:

NSDictionary *dict = @{@"name":@"Jack",@"age":@23,@"sex":@"male",};
NSError *error;
Person *person = [[Person alloc] initWithDictionary:dict error:&error];

输出:

 [name]: Jack[age]: 23[gender]: male

没有受到传入字典里key值的变化的影响。

3,自定义错误

除了一些框架自己处理的错误(比如传入的对象不是字典),框架的作者也允许我们自己定义属于我们自己的错误。
比如,当age对应的数值小于25的时候,打印出Too young!,并阻止模型的转换

在模型的实现文件中:

- (BOOL)validate:(NSError **)error
{if (![super validate:error])return NO;if (self.age < 25){*error = [NSError errorWithDomain:@"Too young!" code:10 userInfo:nil];NSError *errorLog = *error;NSLog(@"%@",errorLog.domain);return NO;}return YES;
}

如果要被转化的数据age小于25,就会打印错误,并且模型也不会转化。

4,模型嵌套

有时,我们需要在模型里加一个数组,而这个数组里面的元素是另一个对象:这就涉及到了模型的嵌套。如果这样做我们需要在模型类中增加一个协议:

#import "JSONModel.h"
@protocol Friend;
@interface Friend : JSONModel
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@end
@interface Person : JSONModel
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, copy) NSString *gender;
@property (nonatomic, strong) NSArray *friends;//数组,嵌套模型
@end

而且要在Person的实现文件里加上这一段代码:

@implementation Friend
@end

不想因为服务器的某个值没有返回(nil)就使程序崩溃, 我们会加关键字Optional

@property (nonatomic, copy) NSString<optional> *name;

如果不想每一条属性都添加,我们也可以在.m文件中重写方法

+ (BOOL) propertyIsOptional:(NSString *)propertyName {return YES;
}

二,源码分析

1,jsonModel的四个基本的使用方法的源码

-(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 inputif (!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 inputif (!data) {if (err) *err = [JSONModelError errorInputIsNil];return nil;}//read the jsonJSONModelError* initError = nil;id obj = [NSJSONSerialization JSONObjectWithData:dataoptions:kNilOptionserror:&initError];if (initError) {if (err) *err = [JSONModelError errorBadJSON];return nil;}//init with dictionaryid objModel = [self initWithDictionary:obj error:&initError];if (initError && err) *err = initError;return objModel;
}
-(id)initWithDictionary:(NSDictionary*)dict error:(NSError**)err
{//check for nil inputif (!dict) {if (err) *err = [JSONModelError errorInputIsNil];return nil;}//invalid input, just create empty instanceif (![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 instanceself = [self init];if (!self) {//super init didn't succeedif (err) *err = [JSONModelError errorModelIsInvalid];return nil;}//check incoming data structureif (![self __doesDictionary:dict matchModelWithKeyMapper:self.__keyMapper error:err]) {return nil;}//import the data from a dictionaryif (![self __importDictionary:dict withKeyMapper:self.__keyMapper validation:YES error:err]) {return nil;}//run any custom model validationif (![self validate:err]) {return nil;}//model is valid! yay!return self;
}

这些方法从上到下依次实现嵌套,最终都实现了

-(id)initWithDictionary:(NSDictionary*)dict error:(NSError**)err

2,具体看一看initWithDictionary:error:

-(id)initWithDictionary:(NSDictionary*)dict error:(NSError**)err
{//方法1. 参数为nilif (!dict) {if (err) *err = [JSONModelError errorInputIsNil];return nil;}//方法2. 参数不是nil,但也不是字典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;}//方法3. 初始化self = [self init];if (!self) {//初始化失败if (err) *err = [JSONModelError errorModelIsInvalid];return nil;}//方法4. 检查用户定义的模型里的属性集合是否大于传入的字典里的key集合(如果大于,则返回NO)if (![self __doesDictionary:dict matchModelWithKeyMapper:self.__keyMapper error:err]) {return nil;}//方法5. 核心方法:字典的key与模型的属性的映射if (![self __importDictionary:dict withKeyMapper:self.__keyMapper validation:YES error:err]) {return nil;}//方法6. 可以重写[self validate:err]方法并返回NO,让用户自定义错误并阻拦model的返回if (![self validate:err]) {return nil;}//方法7. 终于通过了!成功返回modelreturn self;
}

这个方法的大致流程如下:

首先,在这个模型类的对象被初始化的时候,遍历自身到所有的父类(直到JSONModel为止),获取所有的属性,并将其保存在一个字典里。获取传入字典的所有key,将这些key与保存的所有属性进行匹配(__doesDictionary:matchModelWithKeyMapper:error:),如果匹配成功,则对
将字典的相应值赋值给属性(kvc赋值)(__importDictionary:withKeyMapper:validation:error:)

3,JSONModel所持有的一些关联对象名称:

#pragma mark - associated objects names
static const char * kMapperObjectKey;
static const char * kClassPropertiesKey;
static const char * kClassRequiredPropertyNamesKey;
static const char * kIndexPropertyNameKey;

这些关联对象名称的作用如下:

  1. kMapperObjectKey:与JSON映射器相关联的对象的关联对象键。在这种情况下,它可能用于将JSON映射器对象与类或实例关联起来。JSON映射器通常用于将JSON数据映射到模型对象的属性上。

  2. kClassPropertiesKey:与类属性相关联的对象的关联对象键。在这个上下文中,它可能用于将类属性的信息或描述与类对象关联起来。这些信息可能包括属性名称、类型、访问权限等。

  3. kClassRequiredPropertyNamesKey:与类的必需属性名称相关联的对象的关联对象键。在这个上下文中,它可能用于将类的必需属性名称与类对象关联起来。必需属性是指在模型对象中必须存在的属性。

  4. kIndexPropertyNameKey:与索引属性名称相关联的对象的关联对象键。在这个上下文中,它可能用于将索引属性名称与类对象关联起来。索引属性通常用于在模型对象集合中标识唯一的对象。

这些关联对象名称的作用是在运行时为类或实例关联附加的元数据或相关信息。通过使用关联对象,可以将自定义的数据与类或实例相关联,以提供额外的功能或元数据,例如属性映射信息、必需属性列表等。这样可以在运行时动态地访问和操作这些附加的信息。

具体的流程:

4,load方法

定义了该框架支持的类型:

+(void)load
{static dispatch_once_t once;dispatch_once(&once, ^{@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];//自己的类型JSONModelClass = NSClassFromString(NSStringFromClass(self));}});
}
init方法:
-(id)init
{self = [super init];if (self) {[self __setup__];}return self;
}-(void)__setup__
{//只有第一次实例化时,才执行if (!objc_getAssociatedObject(self.class, &kClassPropertiesKey)) {[self __inspectProperties];}//如果存在自定义的mapper,则将它保存在关联对象里面,key是kMapperObjectKeyid mapper = [[self class] keyMapper];if ( mapper && !objc_getAssociatedObject(self.class, &kMapperObjectKey) ) {objc_setAssociatedObject(self.class,&kMapperObjectKey,mapper,OBJC_ASSOCIATION_RETAIN // This is atomic);}
}
-(JSONKeyMapper*)__keyMapper
{//get the model key mapperreturn objc_getAssociatedObject(self.class, &kMapperObjectKey);
}
init中的__inspectProperties方法
-(void)__inspectProperties
{
//    最终保存所有属性的字典,形式为:
//    {
//        age = "@property primitive age (Setters = [])";
//        friends = "@property NSArray*<Friend> 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<Friend>",&,N,V_friendsNSArray* attributeItems = [propertyAttributes componentsSeparatedByString:@","];//说明是只读属性,不做任何操作if ([attributeItems containsObject:@"R"]) {continue; //to next property}//检查出是布尔值if ([propertyAttributes hasPrefix:@"Tc,"]) {p.structName = @"BOOL";//使其变为结构体}//实例化一个scannerscanner = [NSScanner scannerWithString: propertyAttributes];[scanner scanUpToString:@"T" intoString: nil];[scanner scanString:@"T" intoString:nil];//http://blog.csdn.net/kmyhy/article/details/8258858if ([scanner scanString:@"@\"" intoString: &propertyType]) {//属性是一个对象[scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@"\"<"]intoString:&propertyType];//propertyType -> NSStringp.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 popobjc_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:qpropertyType = 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);}//忽略blockif ([propertyType isEqualToString:@"Block"]) {p = nil;}//如果字典里不存在,则添加到属性字典里(终于添加上去了。。。)if (p && ![propertyIndex objectForKey:p.name]) {[propertyIndex setValue:p forKey:p.name];}//setter 和 getterif (p){   //name ->NameNSString *name = [p.name stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:[p.name substringToIndex:1].uppercaseString];// getterSEL getter = NSSelectorFromString([NSString stringWithFormat:@"JSONObjectFor%@", name]);if ([self respondsToSelector:getter])p.customGetter = getter;// settersp.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的所有父类的属性,并将这些属性保存到一个字典中。在将来用于和传入的字典进行映射
该字典的格式如下:

{propertyName1 = "@property propertyType1 (propertyAttributes1)";propertyName2 = "@property propertyType2 (propertyAttributes2)";...
}

JSONModelClassProperty类封装了JSONModel的每一个属性,每个属性都以属性名称作为键,以属性的类型和属性特性作为值进行保存。属性的类型和特性信息是通过扫描属性的属性字符串来获取的。

5,__doesDictionary:matchModelWithKeyMapper:error:方法

-(BOOL)__doesDictionary:(NSDictionary*)dict matchModelWithKeyMapper:(JSONKeyMapper*)keyMapper error:(NSError**)err
{//拿到字典里所有的keyNSArray* incomingKeysArray = [dict allKeys];//返回保存所有属性名称的数组(name,age,gender...)NSMutableSet* requiredProperties = [self __requiredPropertyNames].mutableCopy;//从array拿到setNSSet* incomingKeys = [NSSet setWithArray: incomingKeysArray];//如果用户自定义了mapper,则进行转换if (keyMapper || globalKeyMapper) {NSMutableSet* transformedIncomingKeys = [NSMutableSet setWithCapacity: requiredProperties.count];NSString* transformedName = nil;//便利需要转换的属性列表for (JSONModelClassProperty* property in [self __properties__]) {//被转换成的属性名称 gender(模型内) -> sex(字典内)transformedName = (keyMapper||globalKeyMapper) ? [self __mapString:property.name withKeyMapper:keyMapper] : property.name;//拿到sex以后,查看传入的字典里是否有sex对应的值id value;@try {value = [dict valueForKeyPath:transformedName];}@catch (NSException *exception) {value = dict[transformedName];}//如果值存在,则将sex添加到传入的keys数组中if (value) {[transformedIncomingKeys addObject: property.name];}}incomingKeys = transformedIncomingKeys;}//查看当前的model的属性的集合是否大于传入的属性集合,如果是,则返回错误。//也就是说模型类里的属性是不能多于传入字典里的key的,例如:if (![requiredProperties isSubsetOfSet:incomingKeys]) {//获取多出来的属性[requiredProperties minusSet:incomingKeys];//not all required properties are in - invalid inputJMLog(@"Incoming data was invalid [%@ initWithDictionary:]. Keys missing: %@", self.class, requiredProperties);if (err) *err = [JSONModelError errorInvalidDataWithMissingKeys:requiredProperties];return NO;}//不需要了,释放掉incomingKeys= nil;requiredProperties= nil;return YES;
}

这段代码是一个方法的实现,

  1. 其作用是检查传入的字典(NSDictionary)是否与当前模型类的属性匹配。
  2. 首先,它获取传入字典的所有键(keys)并保存在incomingKeysArray数组中。
  3. 然后,它获取当前模型类的必需属性名称并保存在requiredProperties的可变集合中。
  4. 接下来,它将incomingKeysArray数组转换为集合类型的incomingKeys
  5. 如果存在自定义的键映射器(keyMapper)或全局键映射器(globalKeyMapper),则使用键映射器将属性名称进行转换。它遍历模型类的属性列表,将每个属性的名称根据键映射器进行转换,然后检查传入字典中是否存在转换后的键对应的值。如果存在值,则将原始属性名称添加到transformedIncomingKeys集合中。
  6. 最后,它将transformedIncomingKeys集合赋值给incomingKeys,并检查当前模型类的必需属性是否都存在于incomingKeys集合中。如果有必需属性缺失,则记录错误信息,并返回NO。如果所有必需属性都存在于incomingKeys集合中,则返回YES表示匹配成功。

总之,这段代码用于验证传入字典与当前模型类的属性之间的匹配关系,并返回匹配结果

如果存在了用户自定义的mapper,则需要按照用户的定义来进行转换。(在这里是将gender转换为了sex)。

6,__importDictionary:withKeyMapper:validation:error:方法

-(BOOL)__importDictionary:(NSDictionary*)dict withKeyMapper:(JSONKeyMapper*)keyMapper validation:(BOOL)validation error:(NSError**)err
{//遍历保存的所有属性的字典for (JSONModelClassProperty* property in [self __properties__]) {//将属性的名称拿过来,作为key,用这个key来查找传进来的字典里对应的值NSString* jsonKeyPath = (keyMapper||globalKeyMapper) ? [self __mapString:property.name withKeyMapper:keyMapper] : property.name;//用来保存从字典里获取的值id jsonValue;@try {jsonValue = [dict valueForKeyPath: jsonKeyPath];}@catch (NSException *exception) {jsonValue = dict[jsonKeyPath];}//字典不存在对应的keyif (isNull(jsonValue)) {//如果这个key是可以不存在的if (property.isOptional || !validation) continue;//如果这个key是必须有的,则返回错误if (err) {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 allowedJMLog(@"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;}//如果是兼容的类型:if (property) {// 查看是否有自定义setter,并设置if ([self __customSetValue:jsonValue forProperty:property]) {continue;};// 基本类型if (property.type == nil && property.structName==nil) {//kvc赋值if (jsonValue != [self valueForKey:property.name]) {[self setValue:jsonValue forKey: property.name];}continue;}// 如果传来的值是空,即使当前的属性对应的值不是空,也要将空值赋给它if (isNull(jsonValue)) {if ([self valueForKey:property.name] != nil) {[self setValue:nil forKey: property.name];}continue;}// 1. 属性本身是否是jsonmodel类型if ([self __isJSONModelSubClass:property.type]) {//通过自身的转模型方法,获取对应的值JSONModelError* initErr = nil;id value = [[property.type alloc] initWithDictionary: jsonValue error:&initErr];if (!value) {//如果该属性不是必须的,则略过if (property.isOptional || !validation) continue;//如果该属性是必须的,则返回错误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];}continue;} else {// 如果不是jsonmodel的类型,则可能是一些普通的类型:NSArray,NSString。。。// 是否是模型嵌套(带有协议)if (property.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;}}// 对象类型if (property.isStandardJSONType && [jsonValue isKindOfClass: property.type]) {//可变类型if (property.isMutable) {jsonValue = [jsonValue mutableCopy];}//赋值if (![jsonValue isEqual:[self valueForKey:property.name]]) {[self setValue:jsonValue forKey: property.name];}continue;}// 当前的值的类型与对应的属性的类型不一样的时候,需要查看用户是否自定义了转换器(例如从NSSet到NSArray转换:- (NSSet *)NSSetFromNSArray:(NSArray *)array)if ((![jsonValue isKindOfClass:property.type] && !isNull(jsonValue))||//the property is mutableproperty.isMutable||//custom struct propertyproperty.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 classesNSString* selectorName = [NSString stringWithFormat:@"%@From%@:",(property.structName? property.structName : property.type), //target namesourceClass]; //source nameSEL selector = NSSelectorFromString(selectorName);//查看自定义的转换器是否存在BOOL foundCustomTransformer = NO;if ([valueTransformer respondsToSelector:selector]) {foundCustomTransformer = YES;} else {//try for hidden custom transformerselectorName = [NSString stringWithFormat:@"__%@",selectorName];selector = NSSelectorFromString(selectorName);if ([valueTransformer respondsToSelector:selector]) {foundCustomTransformer = YES;}}//如果存在自定义转换器,则进行转换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 {//没有自定义转换器,返回错误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;
}

这个方法作用是将传入的NSDictionary对象(dict)中的数据映射到当前对象(self)的属性上,并进行验证和转换。

具体功能如下:

  1. 遍历当前对象的所有属性(通过[self properties]获取属性列表)。
  2. 获取属性的名称,并根据keyMapper映射规则将其转换为对应的JSON键路径(jsonKeyPath)。
  3. 从传入的字典(dict)中根据键路径(jsonKeyPath)获取对应的值(jsonValue)。
  4. 如果值(jsonValue)为null或字典中不存在对应的键,则根据属性的可选性和验证选项判断是否继续。
  • 如果属性是可选的或者验证选项为false,则继续下一个属性的处理。
  • 如果属性是必需的且验证选项为true,则返回错误信息。
  1. 检查获取到的值(jsonValue)的类型是否兼容当前属性的类型。
  • 如果值的类型不兼容,则返回错误信息。
  1. 对于兼容的类型:
  • 如果属性存在自定义的setter方法,则调用该方法设置值,并继续下一个属性的处理。
  • 对于基本类型的属性,使用KVC赋值。
  • 如果传来的值为空,即使属性的值不为空,也将空值赋给属性。
  • 如果属性是JSONModel类型,通过属性的转换方法初始化一个新的对象(value),并将其赋给属性。
  • 如果属性是普通的对象类型(如NSArray、NSString等),根据属性的协议进行转换,并赋值给属性。
  • 如果属性的值类型与属性类型不一致,检查是否存在自定义的转换器,并进行转换。
  • 如果以上情况都不满足,则直接赋值给属性。
  1. 处理完所有属性后,返回YES表示映射和转换成功。

这段代码的作用是将传入的字典数据映射到当前对象的属性上,并进行验证和类型转换,以实现属性与字典数据的对应关系。

三,总结

1,JSONModel的优点:

  1. 简化的数据映射:JSONModel提供了简单而直观的方式来将JSON数据映射到Objective-C对象的属性上。开发人员只需定义模型类和属性映射关系,就能轻松地进行数据映射,而无需手动解析和转换JSON数据。
  2. 自动类型转换:JSONModel能够自动将JSON数据的值转换为合适的Objective-C类型。它支持将JSON数据转换为各种常见的数据类型,如NSString、NSNumber、NSDate等,使开发人员无需手动处理类型转换的细节。
  3. 数据验证:JSONModel允许开发人员在模型类中定义数据验证规则。通过实现验证方法,可以对属性进行验证,并在验证失败时提供错误信息。这有助于确保从JSON数据中获取的值符合预期,并提高数据的完整性和可靠性。
  4. 嵌套对象支持:JSONModel支持嵌套的数据结构,可以将一个JSONModel对象作为另一个JSONModel对象的属性。这使得处理复杂的嵌套JSON数据变得简单而直观,可以轻松地创建层次结构的对象模型。
  5. 灵活的属性映射:JSONModel允许开发人员通过自定义属性映射规则来灵活处理属性与JSON键之间的映射关系。可以使用自定义的映射规则,使属性名和JSON键名之间存在不一致,从而适应不同的数据命名约定。
  6. 运行时特性支持:JSONModel使用运行时特性来动态处理属性。它使用关联对象(Associated Objects)来附加和获取额外的属性。这样可以在运行时动态地管理和访问属性,而无需修改原始类的定义。
  7. 扩展性和定制性:JSONModel提供了丰富的扩展点和定制选项,允许开发人员根据自己的需求进行定制和扩展。可以通过自定义转换器、忽略属性、替代属性名等方式来定制数据映射的行为,以满足各种复杂的数据处理需求。

2,JSONModel源码的具体流程图

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/7168.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python深度学习基于Tensorflow(3)Tensorflow 构建模型

文章目录 数据导入和数据可视化数据集制作以及预处理模型结构低阶 API 构建模型中阶 API 构建模型高阶 API 构建模型保存和导入模型 这里以实际项目CIFAR-10为例&#xff0c;分别使用低阶&#xff0c;中阶&#xff0c;高阶 API 搭建模型。 这里以CIFAR-10为数据集&#xff0c;C…

9.4.k8s的控制器资源(job控制器,cronjob控制器)

目录 一、job控制器 二、cronjob控制器 一、job控制器 job控制器就是一次性任务的pod控制器&#xff0c;pod完成作业后不会重启&#xff0c;其重启策略是&#xff1a;Never&#xff1b; 简单案例 启动一个pod&#xff0c;执行完成一个事件&#xff0c;然后pod关闭&#xff1b;…

初识指针(2)<C语言>

前言 前文介绍完了一些指针基本概念&#xff0c;下面介绍一下&#xff0c;const关键字、指针的运算、野指针的成因以及避免&#xff0c;assert函数等。 目录 const&#xff08;常属性&#xff09; 变量的常属性 指针的常属性 指针的运算 ①指针 -整数 ②指针-指针 ③指针与…

【Osek网络管理测试】[TG3_TC6]等待总线睡眠状态_2

&#x1f64b;‍♂️ 【Osek网络管理测试】系列&#x1f481;‍♂️点击跳转 文章目录 1.环境搭建2.测试目的3.测试步骤4.预期结果5.测试结果 1.环境搭建 硬件&#xff1a;VN1630 软件&#xff1a;CANoe 2.测试目的 验证DUT在满足进入等待睡眠状态的条件时是否进入该状态 …

17 内核开发-内核内部内联汇编学习

​ 17 内核开发-内核内部内联汇编学习 课程简介&#xff1a; Linux内核开发入门是一门旨在帮助学习者从最基本的知识开始学习Linux内核开发的入门课程。该课程旨在为对Linux内核开发感兴趣的初学者提供一个扎实的基础&#xff0c;让他们能够理解和参与到Linux内核的开发过程中…

英伟达推出视觉语言模型:VILA

NVIDIA和MIT的研究人员推出了一种新的视觉语言模型(VLM)预训练框架&#xff0c;名为VILA。这个框架旨在通过有效的嵌入对齐和动态神经网络架构&#xff0c;改进语言模型的视觉和文本的学习能力。VILA通过在大规模数据集如Coy0-700m上进行预训练&#xff0c;采用基于LLaVA模型的…

VBA编程之条件语句

上一篇我们讲述了条件语句以及分支。文章的最后用到了逻辑运算符“And“那么今天我们来聊一聊逻辑运算符和Select……Case结构。 在学习前我们先来了解一下&#xff0c;在生活中我们经常说”这个包括那个“&#xff0c;”你或者他“&#xff0c;”不是“等等。而这里”包括“和…

esp32+mqtt协议+paltformio+vscode+微信小程序+温湿度检测

花费两天时间完成了这个项目&#xff08;不完全是&#xff0c;属于是在resnet模型训练和温湿度检测两头跑......模型跑不出来&#xff0c;又是第一次从头到尾独立玩硬件&#xff0c;属于是焦头烂额了......&#xff0c;完成这个项目后&#xff0c;我的第一反应是写个csdn&#…

[每日AI·0506]巴菲特谈 AI,李飞飞创业,苹果或将推出 AI 功能,ChatGPT 版搜索引擎

AI 资讯 苹果或将推出 AI 功能&#xff0c;随 iPhone 发布2024 年巴菲特股东大会&#xff0c;巴菲特将 AI 类比为核技术 巴菲特股东大会 5 万字实录消息称 OpenAI 将于 5 月 9 日发布 ChatGPT 版搜索引擎路透社消息&#xff0c;斯坦福大学 AI 领军人物李飞飞打造“空间智能”创…

论文辅助笔记:Tempo 之 model.py

0 导入库 import math from dataclasses import dataclass, asdictimport torch import torch.nn as nnfrom src.modules.transformer import Block from src.modules.prompt import Prompt from src.modules.utils import (FlattenHead,PoolingHead,RevIN, )1TEMPOConfig 1.…

【C++】 认识多态 + 多态的构成条件详细讲解

前言 C 目录 1. 多态的概念2 多态的定义及实现2 .1 虚函数&#xff1a;2 .2 虚函数的重写&#xff1a;2 .2.1 虚函数重写的两个例外&#xff1a; 2 .3 多态的两个条件&#xff08;重点&#xff09;2 .4 析构函数为啥写成虚函数 3 新增的两个关键字3.1 final的使用&#xff1a;3…

09_电子设计教程基础篇(电阻)

文章目录 前言一、电阻原理二、电阻种类1.固定电阻1、材料工艺1、线绕电阻2、非线绕电阻1、实心电阻1、有机实心电阻2、无机实心电阻 2、薄膜电阻&#xff08;常用&#xff09;1、碳膜电阻2、合成碳膜电阻3、金属膜电阻4、金属氧化膜电阻5、玻璃釉膜电阻 3、厚膜电阻&#xff0…

vue2实现生成二维码和复制保存图片功能(复制的同时会给图片加文字)

<template><divstyle"display: flex;justify-content: center;align-items: center;width: 100vw;height: 100vh;"><div><!-- 生成二维码按钮和输入二维码的输入框 --><input v-model"url" placeholder"输入链接" ty…

智能家居1 -- 实现语音模块

项目整体框架: 监听线程4&#xff1a; 1. 语音监听线程:用于监听语音指令&#xff0c; 当有语音指令过来后&#xff0c; 通过消息队列的方式给消息处理线程发送指令 2. 网络监听线程&#xff1a;用于监听网络指令&#xff0c;当有网络指令过来后&#xff0c; 通过消息队列的方…

SpringSecurity6 学习

学习介绍 网上关于SpringSecurity的教程大部分都停留在6以前的版本 但是&#xff0c;SpringSecurity6.x版本后的内容进行大量的整改&#xff0c;网上的教程已经不能够满足 最新的版本使用。这里我查看了很多教程 发现一个宝藏课程&#xff0c;并且博主也出了一个关于SpringSec…

【python】条件语句与循环语句

目录 一.条件语句 1.定义 2.条件语句格式 &#xff08;1&#xff09;if &#xff08;2&#xff09;if-else &#xff08;3&#xff09;elif功能 &#xff08;4&#xff09;if嵌套使用 3.猜拳游戏 二.循环语句 1. while循环 2.while嵌套 3.for循环 4.break和conti…

被问了n遍的小程序地理位置权限开通方法

小程序地理位置接口有什么功能&#xff1f; 在平时我们在开发小程序时&#xff0c;难免会需要用到用户的地理位置信息的功能&#xff0c;小程序开发者开放平台新规要求如果没有申请开通微信小程序地理位置接口( getLocation )&#xff0c;但是在代码中却使用到了相关接口&#…

人工智能概述与入门基础简述

人工智能&#xff08;AI&#xff09;是计算机科学的一个分支&#xff0c;它致力于创建能够执行通常需要人类智能的任务的机器。这篇科普文章将全面介绍人工智能的基本概念、发展历程、主要技术、实际应用以及如何入门这一领域。 一、人工智能的定义与发展历程 人工智能的概念…

springboot版本升级,及解决springsecurity漏洞问题

背景&#xff1a; 项目中要解决 Spring Security RegexRequestMatcher 认证绕过漏洞&#xff08;CVE-2022-22978&#xff09; 漏洞问题&#xff0c;并且需要将项目的版本整体升级到boot版本2.1.7&#xff0c;升级改造过程非常的痛苦&#xff0c;一方面对整个框架的代码不是很熟…

六淳科技IPO终止背后:十分着急上市,大额分红,实控人买豪宅

华西证券被暂停保荐业务资格6个月的影响力逐渐显现。 近日&#xff0c;深圳证券交易所披露的信息显示&#xff0c;东莞六淳智能科技股份有限公司&#xff08;下称“六淳科技”&#xff09;及其保荐人撤回上市申请材料。因此&#xff0c;深圳证券交易所决定终止对其首次公开发行…