iOS專案中Json轉Model的坑

搶手的哥發表於2019-01-03

Json轉Model

json轉model,是個開發都會遇到過。都已經9102年了,誰還不會用個第三方框架搞。拿起鍵盤就是幹!

就是幹.png
開啟podfile,把大名頂頂的YYModel寫上,pod install一下。再用上ESJsonFormat,直接根據json,都能把model生成好。

特殊處理

啥?返回的欄位值不是我們所需的 在日常開發中,經常會遇到一些介面欄位返回的值,並不是我所需要的型別的情況,這個時候,我們都會對這個欄位進行處理。 舉個栗子:

/** 錯誤程式碼 */
@property (nonatomic, assign) NSInteger error_code;
/** 錯誤訊息 */
@property (nonatomic, copy) NSString *error_msg;
/** 是否成功 */
@property (nonatomic, assign) BOOL isSuccess;
複製程式碼

介面的json中的error_code欄位,介面會用這個欄位告訴我這次請求是否成功。比方說成功的error_code是1,平時我們為了方便開發,會在model裡自己加一個自定義的屬性isSuccess,來表示本次網路請求回來之後的結果是否成功。通常的做法,要麼重寫error_code的set方法,在set的時候,做一次error_code==1的判斷,將判斷的結果,賦值給isSuccess,要麼就重寫isSuccess的get方法,get的時候,返回error_code==1的結果。 相信這些對於老司機們而言,都屬於常規操作了。那我們來看看坑在什麼地方?

入坑

我們來看這個案例: 介面返回了4個欄位值,每個欄位都用得到,所以新建一個model類來解析。

@interface ExamSubjectVo : NSObject

/**  考試學科ID */
@property (nonatomic, assign) NSInteger examSubjectValue;
/**  考試學科名稱 */
@property (nonatomic, strong) NSString *examSubjectName;
/** 學科分數  */
@property (nonatomic, strong) NSString *subjectScore;
/** 基礎學科Id  */
@property (nonatomic, assign) NSInteger subjectBaseId;
@end
複製程式碼

但是由於有業務需求,且為了方便開發過程區分,需要對考試名稱的欄位examSubjectName為全科或者語數外的情況,要特殊處理。所以,按照一貫的思維,我們要重寫set方法

- (void)setExamSubjectName:(NSString *)examSubjectName{
    _examSubjectName = examSubjectName;
    if ([examSubjectName isEqualToString:@"全科"]) {
        self.subjectBaseId = -100;
    }
    if ([examSubjectName isEqualToString:@"語數外"]) {
        self.subjectBaseId = -200;
    }
}
複製程式碼

乍一看,也沒什麼問題,解析的過程中,把欄位的值轉化為我們需要的。而且真機實測的時候,所有的測試機都沒問題,除了一臺iPhone5之外 就除了一臺iPhone5,debug的時候看到set方法確實也走了,可是最終的subjectBaseId並沒有轉化成-100或者-200,可見subjectBaseId又被json本身的值覆蓋了,也就是說 set方法的執行順序,在不同CPU架構裝置上存在差異。

出坑

那麼如何解決問題呢? 正是因為存在這樣的差異,所以我們只能在model所有的欄位全部set完畢之後,再做一些特殊的欄位處理,那麼如何來處理呢? 翻閱YYModel原始碼,肯定能有所發現,果不其然,有所收穫。

/**
 If the default json-to-model transform does not fit to your model object, implement
 this method to do additional process. You can also use this method to validate the 
 model's properties.
 
 @discussion If the model implements this method, it will be called at the end of
 `+modelWithJSON:`, `+modelWithDictionary:`, `-modelSetWithJSON:` and `-modelSetWithDictionary:`.
 If this method returns NO, the transform process will ignore this model.
 
 @param dic  The json/kv dictionary.
 
 @return Returns YES if the model is valid, or NO to ignore this model.
 */
- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic;
複製程式碼

YYModel提供了這麼個方法,它會在+modelWithJSON:, +modelWithDictionary:, -modelSetWithJSON: and -modelSetWithDictionary:方法結束的時候呼叫。 所以我們對model特殊欄位的處理,都應該放到這個方法去執行

- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic{
    if ([self.examSubjectName isEqualToString:@"全科"]) {
        self.subjectBaseId = -100;
    }
    if ([self.examSubjectName isEqualToString:@"語數外"]) {
        self.subjectBaseId = -200;
    }
    return YES;
}
複製程式碼

這麼一來,問題就解決了。 注意,YYModel還有一個- (NSDictionary *)modelCustomWillTransformFromDictionary:(NSDictionary *)dic; 這個方法很類似,但是執行的時機不一樣,這個方法是在model轉化之前執行,雖不符合本案例的需求,但是很有可能在其他類似的情況能用的上。

相關文章