iOS平台基於KVC的JSON與資料對象綁定
在iOS平台上,要操縱JSON資料並不困難,但是,我們還有更簡單的解決方案,使用KVC,全稱是Key-Value Coding。
假設開發人員(你)開發了一款應用,它的資料來自於外部對Web服務,要從Web服務中取回一些JSON資料,資料如下:
{count: 3, sum: 9.0, average: 3.0}
要從伺服器中擷取資料,需要調用NSJSONSerializationalization的JSONObjectWithData方法,並從解序列化的字典中取回資料,比如:
NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];NSLog(@%d, [[dictionary objectForKey:@count] intValue]); // prints 3RdNSLog(@%.1f, [[dictionary objectForKey:@sum] doubleValue]); // prints 9.0NSLog(@%.1f, [[dictionary objectForKey:@average] doubleValue]); // prints 3.0
但是,上面的值比較分散,在做應用開發時,或許想與強型別的資料對象直接互動,這樣會更加簡單。比如,你或許想要建立一個Statistics統計類,來代表通過Web服務返回的資料類型,如下:
@interface Statistics : NSObject@property (nonatomic) int count;@property (nonatomic) double sum;@property (nonatomic) double average;@end
然後可以從字典中提取值來填充以上的對象:
Statistics *statistics = [[Statistics alloc] init];statistics.count = [[dictionary objectForKey:@count] intValue];statistics.sum = [[dictionary objectForKey:@sum] doubleValue];statistics.average = [[dictionary objectForKey:@average] doubleValue];
為了讓事情更簡單,避免代碼重複,可以把這段代碼放在Statistics類的初始化中:
- (instancetype)initWithDictionary:(NSDictionary *)dictionary { self = [super init]; if (self) { self.count = [[dictionary objectForKey:@count] intValue]; self.sum = [[dictionary objectForKey:@sum] doubleValue]; self.average = [[dictionary objectForKey:@average] doubleValue]; } return self;}
代碼綁定JSON響應到Statistics執行個體,如下:
Statistics *statistics = [[Statistics alloc] initWithDictionary:dictionary];
在任何情況下,你都可以使用此強型別的資料對象的屬性來訪問從伺服器返回的資料:
NSLog(@%d, statistics.count); // prints 3NSLog(@%.1f, statistics.sum); // prints 9.0NSLog(@%.1f, statistics.average); // prints 3.0
上面的代碼工作正常,而且把JSON資料對應到強型別的資料對象是非常適合的方法。
但是,還有更簡單的解決方案:KVC。NSObject的setValuesForKeysWithDictionary:方法可用於將給定字典上所有的值自動對應到對象的屬性。使用這種方法,initWithDictionary:方法簡化如下:
- (instancetype)initWithDictionary:(NSDictionary *)dictionary { self = [super init]; if (self) { [self setValuesForKeysWithDictionary:dictionary]; } return self;}
無需手動映射字典到項到屬性值中,使用適合的名字和類型來聲明屬性就足夠了,下面的代碼中Swift中工作良好:
class Statistics: NSObject { var count: Int = 0 var sum: Double = 0 var average: Double = 0 init(dictionary: [String:AnyObject]) { super.init() setValuesForKeysWithDictionary(dictionary); }}
此外,如果你需要自訂屬性名或屬性值的分配,那麼可以簡單的重寫setValue:forKey:方法。比如,假設伺服器以不同的名字來引用平均屬性:
{count: 3, sum: 9.0, mean: 3.0}
可以重寫setValue:forKey:方法,確保值能正確的映射到屬性中:
- (void)setValue:(id)value forKey:(NSString *)key { if ([key isEqual:@mean]) { key = @average; } [super setValue:value forKey:key];}
最後,你可以使用KVC來忽略你不想要的值。比如,假設伺服器的響應還包含了名為“median”的屬性:
{count: 3, sum: 9.0, average: 3.0, median: 3.0}
由於Statistics類沒有定義“Median”屬性,那麼setValuesForKeysWithDictionary:方法會拋出NSUnknownKeyException異常。要避免拋出此異常,可以簡單的重寫setValue:forUndefinedKey::方法。
- (void)setValue:(id)value forUndefinedKey:(NSString *)key { // No-op}