KeyValueObjectMapping 是一个 Cocoa 框架,它提供了一个简单的方法来处理任何键/值类型,例如 JSON
、XML
、plist
,甚至是常见的 NSDictionary
。无需额外的框架。
它旨在与解析器一起使用,例如:NSJSONSerialization
、JSONKit
、NSXMLParser
和其他资源,主要目标是避免在处理键/值类型时需要进行的繁琐工作。
JSON
、XML
、plist
或简单的 NSDictionary
。NSDictionary
上的键同名,一切都将自动完成。DCCustomInitialize
和 DCCustomParser
。DCObjectMapping
将任何键映射到不遵守约定的特定属性。DCArrayMapping
来告知将要插入的具体元素类型。DCPropertyAggregator
将值聚合到特定属性。NSDate
或如果它是以毫秒形式(自1970年1月1日起的UNIX 时间戳)在 JSON
中发送的,则无需额外配置即可进行解析。NSURL
的属性,则框架将尝试使用 [NSURL URLWithString:]
方法将值作为 NSString
传递。使用 CocoaPods
,这是在 Objective-C 世界中管理依赖项的最简单方法。
使用 iOS-Universal-Framework
由于 KeyValueObjectMapping 使用 iOS-Universal-Framework
来构建和编译项目,您可以轻松地将 iOS-Universal-Framework 生成的 .framework 拖入您的应用程序中,导入头文件 DCKeyValueObjectMapping.h 并开始使用该框架。
#import <KeyValueObjectMapping/DCKeyValueObjectMapping.h>
KeyValueObjectMapping 是一个简单的对象,您需要做的就是创建一个新的对象,然后将一个字典转换为任何类。
假设您有一些像这样的 JSON
{
"id_str": "27924446",
"name": "Diego Chohfi",
"screen_name": "dchohfi",
"location": "São Paulo",
"description": "Instrutor na @Caelum, desenvolvedor de coração, apaixonado por música e cerveja, sempre cerveja.",
"url": "http://about.me/dchohfi",
"protected": false,
"created_at": "Tue Mar 31 18:01:12 +0000 2009"
}
您的 User
模型看起来像这样
@interface User : NSObject
@property(nonatomic, strong) NSString *idStr;
@property(nonatomic, strong) NSString *name;
@property(nonatomic, strong) NSString *screenName;
@property(nonatomic, strong) NSString *location;
@property(nonatomic, strong) NSString *description;
@property(nonatomic, strong) NSURL *url;
@property(nonatomic, strong) BOOL protected;
@property(nonatomic, strong) NSNumber *followersCount;
@property(nonatomic, strong) NSDate *createdAt;
@end
使用任何 JSON
解析器,您需要将这个 NSString
转换为 NSDictionary
表示形式
NSError *error;
NSDictionary *jsonParsed = [NSJSONSerialization JSONObjectWithData:jsonData
options:NSJSONReadingMutableContainers error:&error];
如果您不使用 KeyValueObjectMapping,则需要创建一个 User
类型的实例,并在字典上设置与属性名称相同的所有属性。并在需要时将其转换。
User *user = [[User alloc] init];
[user setIdStr: [jsonParsed objectForKey: @"id_str"]];
[user setName: [jsonParsed objectForKey: @"name"]];
[user setScreenName: [jsonParsed objectForKey: @"screen_name"]];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.datePattern = @"eee MMM dd HH:mm:ss ZZZZ yyyy";
NSDate *date = [formatter dateFromString:@"Sat Apr 14 00:20:07 +0000 2012"];
[user setCreatedAt: date];
枯燥的工作,不是吗?所以,如果您使用 KeyValueObjectMapping,您只需要提供字典和要创建的类,其余的都将自动完成。
DCKeyValueObjectMapping *parser = [DCKeyValueObjectMapping mapperForClass: [Tweet class]];
Tweet *tweet = [parser parseDictionary:jsonParsed];
NSLog(@"%@ - %@", tweet.idStr, tweet.name);
如果您的 NSDate
模式与默认模式不同,默认模式为 @"eee MMM dd HH:mm:ss ZZZZ yyyy"
,则可以配置为使用不同的模式。因此,有一个对象可以向框架添加自定义配置。
使用 DCParserConfiguration
,可以更改某些组件的默认行为,例如解析日期的默认模式。
DCParserConfiguration *config = [DCParserConfiguration configuration];
config.datePattern = @"dd/MM/yyyy";
DCKeyValueObjectMapping *parser = [DCKeyValueObjectMapping mapperForClass: [Tweet class] andConfiguration: config];
如果您的 JSON
有一些特定的键不匹配属性名称,则可以使用 DCObjectMapping
将此键映射到属性,属性类型也可以是特定的对象。
您的 Tweet
模型
@interface Tweet : NSObject
@property(nonatomic, readonly) NSString *idStr;
@property(nonatomic, readonly) NSString *tweetText;
@property(nonatomic, readonly) User *userOwner;
@end
并且收到的 JSON
符合以下结构
{
"id_str": 190957570511478800,
"text": "Tweet text",
"user": {
"name": "Diego Chohfi",
"screen_name": "dchohfi",
"location": "São Paulo"
}
}
使用 DCObjectMapping
,可以解析这个 JSON
并像这样覆盖键名
DCParserConfiguration *config = [DCParserConfiguration configuration];
DCObjectMapping *textToTweetText = [DCObjectMapping mapKeyPath:@"text" toAttribute:@"tweetText" onClass:[Tweet class]];
DCObjectMapping *userToUserOwner = [DCObjectMapping mapKeyPath:@"user" toAttribute:@"userOwner" onClass:[Tweet class]];
[config addObjectMapping:textToTweetText];
[config addObjectMapping:userToUserOwner];
DCKeyValueObjectMapping *parser = [DCKeyValueObjectMapping mapperForClass: [Tweet class] andConfiguration:config];
Tweet *tweetParsed = [parser parseDictionary:json];
NSArray
属性由于Objective-C不支持像Java和其他静态语言那样的类型化集合,我们无法确定集合中元素的类型。但是,可以配置键值对象映射(KeyValueObjectMapping),以便了解将在类的特定属性中添加到集合的元素类型。
因此,如果模型User
有许多推文
@interface User : NSObject
@property(nonatomic, strong) NSString *idStr;
@property(nonatomic, strong) NSString *name;
@property(nonatomic, strong) NSString *screenName;
@property(nonatomic, strong) NSString *location;
@property(nonatomic, strong) NSString *description;
@property(nonatomic, strong) NSURL *url;
@property(nonatomic, strong) BOOL protected;
@property(nonatomic, strong) NSNumber *followersCount;
@property(nonatomic, strong) NSDate *createdAt;
@property(nonatomic, strong) NSArray *tweets;
@end
那么推文看起来是
@interface Tweet : NSObject
@property(nonatomic, strong) NSString *idStr;
@property(nonatomic, strong) NSString *text;
@property(nonatomic, strong) NSDate *createdAt;
@end
并且JSON看起来是
{
"id_str": "27924446",
"name": "Diego Chohfi",
"screen_name": "dchohfi",
"location": "São Paulo",
"description": "Instrutor na @Caelum, desenvolvedor de coração, apaixonado por música e cerveja, sempre cerveja.",
"url": "http://about.me/dchohfi",
"protected": false,
"created_at": "Tue Mar 31 18:01:12 +0000 2009",
"tweets" : [
{
"created_at" : "Sat Apr 14 00:20:07 +0000 2012",
"id_str" : 190957570511478784,
"text" : "Tweet text"
},
{
"created_at" : "Sat Apr 14 00:20:07 +0000 2012",
"id_str" : 190957570511478784,
"text" : "Tweet text"
}
]
}
使用DCArrayMapping
并将其添加到配置中,您就可以告诉KeyValueObjectMapping
如何解析特定的属性。
DCArrayMapping *mapper = [DCArrayMapping mapperForClassElements: :[Tweet class] forAttribute:@"tweets"] onClass:[User class]];
DCParserConfiguration *config = [DCParserConfiguration configuration];
[config addArrayMapper:mapper];
DCKeyValueObjectMapping *parser = [[DCKeyValueObjectMapping mapperForClass:[User class] andConfiguration:configuration];
User *user = [parser parseDictionary:jsonParsed];
有时你面临的解析JSON没有权限修改结构体,并且你不想使你的类遵循特定的结构体。使用DCPropertyAggregator
,你可以将一个键值聚合到你的领域模型的特定属性上。
因此,如果你的JSON看起来像这样
{
"tweet" : "Some text",
"latitude" : -23.588453,
"longitude" : -46.632103,
"distance" : 100
}
如果你遵循这个JSON结构,你的对象不会那么有组织,对吗?所以,你可以使你的对象遵循不同的结构
@interface Tweet : NSObject
@property(nonatomic, readonly) NSString *text;
@property(nonatomic, readonly) Location *location;
@end
@interface Location : NSObject
@property(nonatomic, readonly) NSNumber *distance;
@property(nonatomic, readonly) Point *point;
@end
@interface Point : NSObject
@property(nonatomic, readonly) NSNumber *latitude;
@property(nonatomic, readonly) NSNumber *longitude;
@end
并且使用DCPropertyAggregator
来映射这个特定的行为
DCPropertyAggregator *aggregteLatLong = [DCPropertyAggregator aggregateKeys:[NSSet setWithObjects:@"latitude", @"longitude", nil] intoAttribute:@"point"];
DCPropertyAggregator *aggregatePointDist = [DCPropertyAggregator aggregateKeys:[NSSet setWithObjects:@"point", @"distance", nil] intoAttribute:@"location"];
DCParserConfiguration *configuration = [DCParserConfiguration configuration];
[configuration addAggregator:aggregteLatLong];
[configuration addAggregator:aggregatePointDist];
DCKeyValueObjectMapping *parser = [DCKeyValueObjectMapping mapperForClass:[Tweet class] andConfiguration:configuration];
Tweet *tweet = [parser parseDictionary: json];