JSONModel

每个IOS开发人员对于JSONModel都是应该比较熟悉的,这次达内科技的金牌讲师就打算专门谈谈它,掌握这个JSONModel对于ios培训的同学们来说是举足轻重的。
  JSONModel是一个解析服务器返回的Json数据的库。
  通常服务器传回的json数据要通过写一个数据转换模块将NSDictionary转换为Model,将NSString数据转换为Model中property的数据类型。
  这样服务器如果要做修改,可能需要改两三个文件。
  JSONModel的出现就是为了将这种解析工作在设计层面完成。
  对其源码的核心部分JSONModel.m做了源码阅读,笔记如下:
  在
  -(id)initWithDictionary:(NSDictionary*)dicterror:(NSError**)err
  函数中完成所有解析工作:如果有任何失误或者错误直接返回nil。
  {
  //1、做有效性判断(dict是不是空啊,dict是不是真是一个NSDictionary)
  //checkfornilinput
  if(!dict){
  if(err)*err=[JSONModelErrorerrorInputIsNil];
  returnnil;
  }
  //invalidinput,justcreateemptyinstance
  if(![dictisKindOfClass:[NSDictionaryclass]]){
  if(err)*err=[JSONModelErrorerrorInvalidData];
  //createaclassinstance
  self=[superinit];
  if(!self){
  //superinitdidn'tsucceed
  if(err)*err=[JSONModelErrorerrorModelIsInvalid];
  //__setup__中通过调用__restrospectProperties建立类属性的映射表,并且存放在全局变量classProperties里面
  //->__restrospectProperties中利用runtimefunction搞出属性列表:
  //->获得属性列表class_copyPropertyList(得到objc_property_t数组)->对于每一个objc_property_t调用property_getName获得名称,property_getAttributes获得属性的描述(字符串)->通过解析字符串获得属性的类型、是否是Mutable、是否是基本的JSON类型等等
  //->调用[classsuperclass]获得父类继续获取列表
  //->列表保存在classProperties中备用
  //->调用+keyMapper获得key转换列表,生成JSONKeyMapper对象存入keyMapper。
  //doinitialclasssetup,retrospecproperties
  [self__setup__];
  //看看必传参数中是否在输入参数中都有。
  //checkifallrequiredpropertiesarepresent
  NSArray*incomingKeysArray=[dictallKeys];
  NSMutableSet*requiredProperties=[self__requiredPropertyNames];
  NSSet*incomingKeys=[NSSetsetWithArray:incomingKeysArray];
  //getthekeymapper
  JSONKeyMapper*keyMapper=keyMappers[__className_];
  //transformthekeynames,ifneccessary
  if(keyMapper){
  //对比dict输入的keyName导入NSSet与keyMapper中JSONKeyMapper对象做keyName的转换。统一转换为对象的propertyname。
  NSMutableSet*transformedIncomingKeys=[NSMutableSetsetWithCapacity:requiredProperties.count];
  NSString*transformedName=nil;
  //loopovertherequiredpropertieslist
  for(NSString*requiredPropertyNameinrequiredProperties){
  //getthemappedkeypath
  transformedName=keyMapper.modelToJSONKeyBlock(requiredPropertyName);
  //chekifexistsandifso,addtoincomingkeys
  if([dictvalueForKeyPath:transformedName]){
  [transformedIncomingKeysaddObject:requiredPropertyName];
  //overwritetherawincominglistwiththemappedkeynames
  incomingKeys=transformedIncomingKeys;

  JSONModel(二)
//利用NSSet的isSubsetOfSet:将必传参数表与输入的keyName表对比。如果不是包含关系说明参数传的不够。  //checkformissinginputkeys
  if(![requiredPropertiesisSubsetOfSet:incomingKeys]){
  //getalistofthemissingproperties
  [requiredPropertiesminusSet:incomingKeys];
  //notallrequiredpropertiesarein-invalidinput
  JMLog(@"Incomingdatawasinvalid[%@initWithDictionary:].Keysmissing:%@",self._className_,requiredProperties);
  if(err)*err=[JSONModelErrorerrorInvalidDataWithMissingKeys:requiredProperties];  returnnil;
  }
  //notneededanymore
  incomingKeys=nil;
  requiredProperties=nil;
  //从对象的classProperties列表中循环到dict中取值:(赋值使用KVO操作的setValue:forKey:来做的,这样会直接调用setter函数赋值)
  //loopovertheincomingkeysandsetself'sproperties
  for(JSONModelClassProperty*propertyin[self__properties__]){
  //对于每一个对象的property,通过keyMapper的转换找到对应dictproperty的dictKeyPath,找到值jsonValue。如果没有值,并且这个属性是Optional的就进行下一项property对比。
  //convertkeynameotmodelkeys,ifamapperisprovided
  NSString*jsonKeyPath=property.name;
  if(keyMapper)jsonKeyPath=keyMapper.modelToJSONKeyBlock(property.name);
  //JMLog(@"keyPath:%@",jsonKeyPath);
  //generalcheckfordatatypecompliance
  idjsonValue=[dictvalueForKeyPath:jsonKeyPath];
  //checkforOptionalproperties
  if(jsonValue==nil&&property.isOptional==YES){
  //skipthisproperty,continuewithnextproperty  continue;
  //对找到的值做类型判断,如果不是JSON应该返回的数据类型就报错。(注意:NSNull是可以作为参数回传的)
  ClassjsonValueClass=[jsonValueclass];
  BOOLisValueOfAllowedType=NO;
  for(ClassallowedTypeinallowedJSONTypes){
  if([jsonValueClassisSubclassOfClass:allowedType]){
  isValueOfAllowedType=YES;
  break;
  if(isValueOfAllowedType==NO){
  //typenotallowed
  JMLog(@"Type%@isnotallowedinJSON.",NSStringFromClass(jsonValueClass));
  if(err)*err=[JSONModelErrorerrorInvalidData];
  returnnil;
  //checkifthere'smatchingpropertyinthemodel
  //JSONModelClassProperty*property=classProperties[self.className][key];
  //接着对property的属性与jsonValue进行类型匹配:
  if(property){
  //如果是基本类型(int/float等)直接值拷贝;
  //0)handleprimitives
  if(property.type==nil&&property.structName==nil){
  //justcopythevalue
  [selfsetValue:jsonValueforKey:property.name];
  //skipdirectlytothenextkey
  continue;
  //如果是NSNull直接赋空值;
  //0.5)handlenils
  if(isNull(jsonValue)){
  [selfsetValue:nilforKey:property.name];
  //如果是值也是一个JsonModel,递归搞JsonModel
  //1)checkifpropertyisitselfaJSONModel
  if([[property.typeclass]isSubclassOfClass:[JSONModelclass]]){
  //initializetheproperty'smodel,storeit
  NSError*initError=nil;
  idvalue=[[property.typealloc]initWithDictionary:jsonValueerror:&initError];
  if(!value){
  if(initError&&err)*err=[JSONModelErrorerrorInvalidData];
  [selfsetValue:valueforKey:property.name];
  //forclarity,doesthesamewithoutcontinue
  }else{
  //如果property中有protocol解析将jsonValue按照protocol解析,如NSArray<JsonModelSubclass>,protocol就是JsonModelSubclass
  //2)checkifthere'saprotocoltotheproperty
  //)mightornotbethecasethere'sabuiltintransofrmforit
  if(property.protocol){
  //JMLog(@"proto:%@",p.protocol);
  //__transform:forProperty:函数功能:
  //->先判断下protocolClass是否在运行环境中存在,如不存在并且property是NSArray类型,直接报错。否则,直接返回。
  //->如果protocalClass是JsonModel的子类,
  //->如果property.type是NSArray
  //->判断一下是否是使用时转换
  //->如果为使用时转换则输出一个JSONModelArray(NSArray)的子类
  //->如果不是使用时转换则输出一个NSArray,其中的对象全部转换为protocalClass所对应对象
  //->如果property.type是NSDictionary
  //->将value转换为protocalClass所对应对象
  //->根据key存储到一个NSDictionary中输出
  jsonValue=[self__transform:jsonValueforProperty:property];
  if(!jsonValue){

JSONModel三)
//如果是基本JSON类型(NSString/NSNumber)
  //3.1)handlematchingstandardJSONtypes
  if(property.isStandardJSONType&&[jsonValueisKindOfClass:property.type]){
  //如果是mutable的,做一份MutableCopy
  //mutableproperties
  if(property.isMutable){
  jsonValue=[jsonValuemutableCopy];
  //setthepropertyvalue
  [selfsetValue:jsonValueforKey:property.name];
  continue;
  //如果property.type是NSArray
  //3.3)handlevaluestotransform
  if(
  //如果(类型没有匹配,并且jsonValue不为空)或者是Mutable的property(说明是特殊类型转换)
  (![jsonValueisKindOfClass:property.type]&&!isNull(jsonValue))
  ||
  //thepropertyismutable
  property.isMutable
  ){
  //利用JSONValueTransformer找到源类型
  //searchedaroundthewebhowtodothisbetter
  //butdidnotfindanysolution,maybethat'sthebestidea?(hardly)
  ClasssourceClass=[JSONValueTransformerclassByResolvingClusterClasses:[jsonValueclass]];
  //JMLog(@"totype:[%@]fromtype:[%@]transformer:[%@]",p.type,sourceClass,selectorName);
  //用字符串拼出转换函数的名称字符串,到JSONValueTransformer中去搜索@SEL执行出正确类型
  //buildamethodselectorforthepropertyandjsonobjectclasses
  NSString*selectorName=[NSStringstringWithFormat:@"%@From%@:",
  (property.structName?property.structName:property.type),//targetname
  sourceClass];//sourcename
  SELselector=NSSelectorFromString(selectorName);
  //checkifthere'satransformerwiththatname
  if([valueTransformerrespondsToSelector:selector]){
  //it'sOK,believeme...
  #pragmaclangdiagnosticpush
  #pragmaclangdiagnosticignored"-Warc-performSelector-leaks"
  //transformthevalue
  jsonValue=[valueTransformerperformSelector:selectorwithObject:jsonValue];
  #pragmaclangdiagnosticpop
  }else{
  //it'snotaJSONdatatype,andthere'snotransformerforit
  //ifpropertytypeisnotsupported-that'saprogrammermistaked->exception
  @throw[NSExceptionexceptionWithName:@"Typenotallowed"
  reason:[NSStringstringWithFormat:@"%@typenotsupportedfor%@.%@",property.type,[selfclass],property.name]
  userInfo:nil];
  //哪儿都不是的直接存起来
  //3.4)handle"allother"cases(ifany)
  //最后调用validate:看看结果是不是有效,没问题就返回了。
  //runanycustommodelvalidation
  NSError*validationError=nil;
  BOOLdoesModelDataValidate=[selfvalidate:&validationError];
  if(doesModelDataValidate==NO){
  if(err)*err=validationError;
  //modelisvalid!yay!
  returnself;
  程序亮点:
  1、为了提高效率通过static的NSArray和NSDictionary进行解耦。
  2、JSONValueTransformer实现了一个可复用的类型转换模板。
  3、通过runtimefunction解析出property列表,通过property相关函数解析出名称,和Attributes的信息。
  4、NSScanner的使用
  5、NSSet的包含关系判断两个集合的交集
  6、利用[NSObjectsetValue:forKey:]的KVO操作赋值,可以直接调用setter函数,并且可以赋nil到property中
  7、适时给子类一个函数可以修改父类的一些行为
  聪明在于勤奋,天才在于积累,对于学习语言来说,勤奋和积累是非常必要的,把一个技术点悟透,只有反复不断的练习,达内希望每一位同学都能把学习当成一种信仰,不断的充实自己完善自己。

相关文章

文章浏览阅读2.4k次。最近要优化cesium里的热力图效果,浏览...
文章浏览阅读1.2w次,点赞3次,收藏19次。在 Python中读取 j...
文章浏览阅读1.4k次。首字母缩略词 API 代表应用程序编程接口...
文章浏览阅读802次,点赞10次,收藏10次。解决一个JSON反序列...
文章浏览阅读882次。Unity Json和Xml的序列化和反序列化_uni...