標籤:blog http io ar os 使用 sp for on
JSONModel是一個解析伺服器返回的Json資料的庫。
http://blog.csdn.net/dyllove98/article/details/9050905
通常伺服器傳回的json資料要通過寫一個資料轉換模組將NSDictionary轉換為Model,將NSString資料轉換為Model中property的資料類型。
這樣伺服器如果要做修改,可能需要改兩三個檔案。
JSONModel的出現就是為了將這種解析工作在設計層面完成。
使用方法:參考串連
對其源碼的核心部分JSONModel.m做了源碼閱讀,筆記如下:
在
-(id)initWithDictionary:(NSDictionary*)dict error:(NSError**)err
函數中完成所有解析工作:如果有任何失誤或者錯誤直接返回nil。
-(id)initWithDictionary:(NSDictionary*)dict error:(NSError**)err{ //1、做有效性判斷(dict是不是空啊,dict是不是真是一個NSDictionary) //check for nil input if (!dict) { if (err) *err = [JSONModelError errorInputIsNil]; return nil; } //invalid input, just create empty instance if (![dict isKindOfClass:[NSDictionary class]]) { if (err) *err = [JSONModelError errorInvalidData]; return nil; } //create a class instance self = [super init]; if (!self) { //super init didn‘t succeed if (err) *err = [JSONModelError errorModelIsInvalid]; return nil; } //__setup__中通過調用__restrospectProperties建立類屬性的映射表,並且存放在全域變數classProperties裡面 //->__restrospectProperties中利用runtime function搞出屬性列表: // ->獲得屬性列表class_copyPropertyList(得到objc_property_t數組)->對於每一個objc_property_t調用property_getName獲得名稱,property_getAttributes獲得屬性的描述(字串)->通過解析字串獲得屬性的類型、是否是Mutable、是否是基本的JSON類型等等 // ->調用[class superclass]獲得父類繼續擷取列表 // ->列表儲存在classProperties中備用 //->調用+keyMapper獲得key轉換列表,產生JSONKeyMapper對象存入keyMapper。 //do initial class setup, retrospec properties [self __setup__]; //看看必傳參數中是否在輸入參數中都有。 //check if all required properties are present NSArray* incomingKeysArray = [dict allKeys]; NSMutableSet* requiredProperties = [self __requiredPropertyNames]; NSSet* incomingKeys = [NSSet setWithArray: incomingKeysArray]; //get the key mapper JSONKeyMapper* keyMapper = keyMappers[__className_]; //transform the key names, if neccessary if (keyMapper) { //對比dict輸入的keyName匯入NSSet與keyMapper中JSONKeyMapper對象做keyName的轉換。統一轉換為對象的propertyname。 NSMutableSet* transformedIncomingKeys = [NSMutableSet setWithCapacity: requiredProperties.count]; NSString* transformedName = nil; //loop over the required properties list for (NSString* requiredPropertyName in requiredProperties) { //get the mapped key path transformedName = keyMapper.modelToJSONKeyBlock(requiredPropertyName); //chek if exists and if so, add to incoming keys if ([dict valueForKeyPath:transformedName]) { [transformedIncomingKeys addObject: requiredPropertyName]; } } //overwrite the raw incoming list with the mapped key names incomingKeys = transformedIncomingKeys; } //利用NSSet的isSubsetOfSet:將必傳參數表與輸入的keyName表對比。如果不是內含項目關聯性說明參數傳的不夠。 //check for missing input keys if (![requiredProperties isSubsetOfSet:incomingKeys]) { //get a list of the missing properties [requiredProperties minusSet:incomingKeys]; //not all required properties are in - invalid input JMLog(@"Incoming data was invalid [%@ initWithDictionary:]. Keys missing: %@", self._className_, requiredProperties); if (err) *err = [JSONModelError errorInvalidDataWithMissingKeys:requiredProperties]; return nil; } //not needed anymore incomingKeys= nil; requiredProperties= nil; //從對象的classProperties列表中迴圈到dict中取值:(賦值使用KVO操作的setValue:forKey:來做的,這樣會直接調用setter函數賦值) //loop over the incoming keys and set self‘s properties for (JSONModelClassProperty* property in [self __properties__]) { //對於每一個對象的property,通過keyMapper的轉換找到對應dict property的dictKeyPath,找到值jsonValue。如果沒有值,並且這個屬性是Optional的就進行下一項property對比。 //convert key name ot model keys, if a mapper is provided NSString* jsonKeyPath = property.name; if (keyMapper) jsonKeyPath = keyMapper.modelToJSONKeyBlock( property.name ); //JMLog(@"keyPath: %@", jsonKeyPath); //general check for data type compliance id jsonValue = [dict valueForKeyPath: jsonKeyPath]; //check for Optional properties if (jsonValue==nil && property.isOptional==YES) { //skip this property, continue with next property continue; } //對找到的值做類型判斷,如果不是JSON應該返回的資料類型就報錯。(注意:NSNull是可以作為參數回傳的) 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) *err = [JSONModelError errorInvalidData]; return nil; } //check if there‘s matching property in the model //JSONModelClassProperty* property = classProperties[self.className][key]; //接著對property的屬性與jsonValue進行類型匹配: if (property) { //如果是基本類型(int/float等)直接值拷貝; // 0) handle primitives if (property.type == nil && property.structName==nil) { //just copy the value [self setValue:jsonValue forKey: property.name]; //skip directly to the next key continue; } //如果是NSNull直接賦空值; // 0.5) handle nils if (isNull(jsonValue)) { [self setValue:nil forKey: property.name]; continue; } //如果是值也是一個JsonModel,遞迴搞JsonModel // 1) check if property is itself a JSONModel if ([[property.type class] isSubclassOfClass:[JSONModel class]]) { //initialize the property‘s model, store it NSError* initError = nil; id value = [[property.type alloc] initWithDictionary: jsonValue error:&initError]; if (!value) { if (initError && err) *err = [JSONModelError errorInvalidData]; return nil; } [self setValue:value forKey: property.name]; //for clarity, does the same without continue continue; } else { //如果property中有protocol解析將jsonValue按照protocol解析,如NSArray<JsonModelSubclass>,protocol就是JsonModelSubclass // 2) check if there‘s a protocol to the property // ) might or not be the case there‘s a built in transofrm for it 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:jsonValue forProperty:property]; if (!jsonValue) { if (err) *err = [JSONModelError errorInvalidData]; return nil; } } //如果是基本JSON類型(NSString/NSNumber) // 3.1) handle matching standard JSON types if (property.isStandardJSONType && [jsonValue isKindOfClass: property.type]) { //如果是mutable的,做一份MutableCopy //mutable properties if (property.isMutable) { jsonValue = [jsonValue mutableCopy]; } //set the property value [self setValue:jsonValue forKey: property.name]; continue; } //如果property.type是NSArray // 3.3) handle values to transform if ( //如果(類型沒有匹配,並且jsonValue不為空白)或者是Mutable的property(說明是特殊類型轉換) (![jsonValue isKindOfClass:property.type] && !isNull(jsonValue)) || //the property is mutable property.isMutable ) { //利用JSONValueTransformer找到源類型 // 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); //用字串拼出轉換函式的名稱字串,到JSONValueTransformer中去搜尋@SEL執行出正確類型 //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 if there‘s a transformer with that name if ([valueTransformer respondsToSelector:selector]) { //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 [self setValue:jsonValue forKey: property.name]; } else { // it‘s not a JSON data type, and there‘s no transformer for it // if property type is not supported - that‘s a programmer mistaked -> exception @throw [NSException exceptionWithName:@"Type not allowed" reason:[NSString stringWithFormat:@"%@ type not supported for %@.%@", property.type, [self class], property.name] userInfo:nil]; return nil; } } else { //哪兒都不是的直接存起來 // 3.4) handle "all other" cases (if any) [self setValue:jsonValue forKey: property.name]; } } } } //最後調用validate:看看結果是不是有效,沒問題就返回了。 //run any custom model validation NSError* validationError = nil; BOOL doesModelDataValidate = [self validate:&validationError]; if (doesModelDataValidate == NO) { if (err) *err = validationError; return nil; } //model is valid! yay! return self;}
程式亮點:
1、為了提高效率通過static的NSArray和NSDictionary進行解耦。
2、JSONValueTransformer實現了一個可複用的類型轉換模板。
3、通過runtime function解析出property列表,通過property相關函數解析出名稱,和Attributes的資訊。
4、NSScanner的使用
5、NSSet的內含項目關聯性判斷兩個集合的交集
6、利用[NSObject setValue:forKey:]的KVO操作賦值,可以直接調用setter函數,並且可以賦nil到property中
7、適時給子類一個函數可以修改父類的一些行為
JSONModel源碼閱讀筆記