由ipodishima开发并维护,他是Wasappli Inc的创始人兼首席技术官。
由Wisembly赞助
从JSON到NSObject
的快速映射器
NSObject
的双向转换访问wiki了解更多关于WAMapping高级使用的详细信息。
WAMapping是一个iOS库,可以将字典转换为对象,将对象转换为字典。它的目标是简化手动解析数据和为对象分配值的大括号模板代码。当涉及到使用CoreData时,尤其是由于插入或更新操作,它变得更困难。我还没有提到它所涉及的性能。WAMapping为您解决了这个问题!
在典型用途中,source
被称作服务器响应转换为字典,而destination
被称作应用值的对象目标,例如一个NSManagedObject
。
让我们假设以下Enterprise
类
@interface Enterprise : NSObject
@property (nonatomic, strong) NSNumber *itemID;
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSDate *creationDate;
@property (nonatomic, strong) NSNumber *streetNumber;
@property (nonatomic, strong) NSArray *employees; // Can be mutable, or an `NSSet` or an `NSOrderedSet`
@property (nonatomic, strong) NSArray *chiefs;
@end
itemID
是在存储中标识你的对象。这不是必需的,但推荐使用,以避免创建重复项。
假设有以下JSON
{
"id": 1,
"name": "Wasappli",
"creation_date": "2013-10-01",
"address": {
"street_number": 5149
}
}
映射看起来如下:
WAEntityMapping *enterpriseMapping = [WAEntityMapping mappingForEntityName:@"Enterprise"];
enterpriseMapping.identificationAttribute = @"itemID";
// Add the classic attributes
[enterpriseMapping addAttributeMappingsFromDictionary:@{
@"id": @"itemID",
@"name": @"name",
@"address.street_number": @"streetNumber"
}];
// Map custom values. Here an `NSDate` from a string using an `NSDateTransformer`
[enterpriseMapping addMappingFromSourceProperty:@"creation_date"
toDestinationProperty:@"creationDate"
withBlock:^id(id value) {
return [dateFormatter dateFromString:value];
}
reverseBlock:^id(id value) {
return [dateFormatter stringFromDate:value];
}];
// Register the mapping for future use
WAMappingRegistrar *registrar = [WAMAppingRegistrar new];
[registrar registerMapping:enterpriseMapping];
// [registrar registerMapping:employeeMapping];
// WAEntityMapping *savedEnterpriseMapping = [registrar mappingForEntityName:@"Enterprise"];
这样就完成了...!
首先,创建一个存储。这是必需的步骤。我在此仓库提供了三种存储方式:
WAMemoryStore
,它依赖于简单的NSMutableSet
,WANSCodingStore
,使用NSCoding
协议保存对象,WACoreDataStore
,利用CoreData
。如果你想要使用SQLite,例如,你可以轻松地创建自己的存储,查看wiki。
WAMemoryStore *store = [[WAMemoryStore alloc] init];
// or
// WACoreDataStore *store = [[WACoreDataStore alloc] initWithManagedObjectContext:localContext];
// or
// WANSCodingStore *store = [[WANSCodingStore alloc] initWithArchivePath:archivePath];
然后,使用存储分配一个映射器
WAMapper *mapper = [[WAMapper alloc] initWithStore:store];
最后,将字典表示映射到对象
[mapper mapFromRepresentation:json
mapping:enterpriseMapping
completion:^(NSArray *mappedObjects) {
firstEnterprise = [mappedObjects firstObject];
}];
就这样解决了!
WAMapping
也支持关系
{
"id": 1,
"first_name": "Marian",
"enterprise": {
"id": 1,
"name": "Wasappli",
"creation_date": "2013-10-01",
"address": {
"street_number": 5149
}
}
}
WARelationshipMapping *enterpriseRelationship =
[WARelationshipMapping relationshipMappingFromSourceProperty:@"enterprise" toDestinationProperty:@"enterprise" withMapping:enterpriseMapping];
[employeeMapping addRelationshipMapping:enterpriseRelationship];
{
"enterprise": {
"id": 1,
"name": "Wasappli",
"creation_date": "2013-10-01",
"address": {
"street_number": 5149
},
"chiefs": 1 # Could also be [1, 2, 3]
},
"employees": [{
"id": 1,
"first_name": "Marian"
}]
}
WARelationshipMapping *chiefsRelationship = [WARelationshipMapping relationshipMappingFromSourceIdentificationAttribute:@"chiefs" toDestinationProperty:@"chiefs" withMapping:employeeMapping];
[enterpriseMapping addRelationshipMapping:chiefsRelationship];
此库还包含了一个反向映射器。它支持从对象到字典的反向转换。
WAReverseMapper *reverseMapper = [[WAReverseMapper alloc] init];
json = [reverseMapper reverseMapObjects:enterprises
fromMapping:enterpriseMapping
shouldMapRelationship:nil];
如果您有一个返回相同格式的所有日期的服务器,那么您只需让映射器或反向映射器转换一次即可。
而不是编写
[enterpriseMapping addAttributeMappingsFromDictionary:@{
@"id": @"itemID",
@"name": @"name",
@"address.street_number": @"streetNumber"
}];
// Map custom values. Here an `NSDate` from a string using an `NSDateTransformer`
[enterpriseMapping addMappingFromSourceProperty:@"creation_date"
toDestinationProperty:@"creationDate"
withBlock:^id(id value) {
return [dateFormatter dateFromString:value];
}
reverseBlock:^id(id value) {
return [dateFormatter stringFromDate:value];
}];
您将编写
[enterpriseMapping addAttributeMappingsFromDictionary:@{
@"id": @"itemID",
@"name": @"name",
@"address.street_number": @"streetNumber",
@"creation_date": @"creationDate"
}];
id(^toDateMappingBlock)(id ) = ^id(id value) {
if ([value isKindOfClass:[NSString class]]) {
return [dateFormatter dateFromString:value];
}
return value;
};
[mapper addDefaultMappingBlock:toDateMappingBlock
forDestinationClass:[NSDate class]];
同样的事情也发生在反向映射器上。请注意,如果您为特定的属性(如只有年份的日期)提供自定义映射在NSDate
对象上,您可以将属性添加到实体映射中,这将覆盖默认行为。
Both WAMapper
和 WAReverseMapper
支持NSProgress。请注意,Apple 明确在其有关NSProgressReporting
(我们在其中模仿)的文档中指出,采用此协议的对象通常会“一次性的”,这意味着您应该为每个映射操作使用一个 WAMapper
。
您可以使用以下小块代码跟踪进度。请注意,进度会计算映射的主顶级对象(如果您的数组中有一个包含千个对象的关系,进度不会反映映射的千个子对象)。这是因为选择之前在iOS 9之前采用子进度不是很好。
[mapper.progress addObserver:self
forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
options:NSKeyValueObservingOptionNew
context:NULL];
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
if ([keyPath isEqualToString:NSStringFromSelector(@selector(fractionCompleted))] && [object isKindOfClass:[NSProgress class]]) {
NSLog(@"Mapping progress = %f", [change[@"new"] doubleValue]);
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
您可以使用以下代码取消映射或反向映射。请注意,要发生取消,您必须从其他线程调用映射!
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
[mapper mapFromRepresentation:JSON mapping:employeeMapping completion:^(NSArray *mappedObjects, NSError *error) {
NSLog(@"Mapped objects %@ - Error %@", mappedObjects, error);
}];
});
[mapper.progress cancel];
您可以从Restkit和FastEasyMapping中获得灵感。这两个都是我在项目上使用过的库,但存在问题。
#贡献:问题、建议、Pull Requests?
如果您遇到特定于WAAppRouting的问题,请在此处打开新问题。
对于新功能的pull requests,我们鼓励并非常重视!请尽量与现有代码风格保持一致。如果您正在考虑对项目进行重大更改或添加,请在打开新问题之前询问,以便有机会合并。
#到此为止!