iOS平台基于KVC的JSON与数据对象绑定
作者:chszs,未经博主允许不得转载。经许可的转载需注明作者和博客主页:http://blog.csdn.net/chszs
在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 3Rd
NSLog(@"%.1f", [[dictionary objectForKey:@"sum"] doubleValue]); // prints 9.0
NSLog(@"%.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 3
NSLog(@"%.1f", statistics.sum); // prints 9.0
NSLog(@"%.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
}