简单的属性映射器,解决了最常见的数据解析问题。
CTXPropertyMapper 利用最近的 Objective-C 运行时新增功能,包括 ARC 和 blocks。它需要 iOS 6 或更高版本。
要使用 CocoaPods 安装,请将以下行添加到您的项目 Podfile 中
pod 'CTXPropertyMapper'
假设以下模型
@interface User : NSObject
@property (nonatomic, strong) NSString *firstName;
@property (nonatomic, strong) NSString *lastName;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, assign) BOOL active;
@property (nonatomic, strong) Job *job;
@property (nonatomic, strong) NSURL *profileURL;
@end
并接收以下 JSON 对象
{
"avatarURL": "http://server.com/avatarurlpath",
"firstName": "Jon",
"lastName": "Snow",
"origin": "Winterfell, The North, Westeros",
"quote":"you know nothing Jon Snow (Ygritte)",
"status":{
"alive":true
},
"job": {
"title":"The bastard of Winterfell",
"sector":"Castle Black",
"hours":"Full Time"
}
}
则可以按如下方式配置属性映射器
CTXPropertyMapper *mapper = [[CTXPropertyMapper alloc] init];
[mapper addMappings:@{@"firstName":CTXProperty(firstName),
@"lastName":CTXProperty(lastName),
@"status.alive":CTXProperty(active)
}
forClass:[User class]];
//Decoding
NSArray *errors = nil;
User *user = [mapper createObjectWithClass:[User class] fromDictionary:dictionary errors:&errors];
//Encoding
NSDictionary *output = [mapper exportObject:user];
CTXPropertyMapper 足够灵活,可以解析复杂和嵌套的对象。
CTXPropertyMapper *mapper = [[CTXPropertyMapper alloc] init];
//URL
CTXValueTransformerBlock decodeURL = ^id(NSString *input, NSString *propertyName){
return [NSURL URLWithString:input];
};
CTXValueTransformerBlock encodeURL = ^id(NSURL *input, NSString *propertyName){
return [input absoluteString];
};
//Origin
CTXValueConsumerBlock decodeOrigin = ^void(NSString *input, User *user){
NSArray *originParts = [input componentsSeparatedByString:@","];
if (originParts.count == 3) {
user.origin = originParts[0];
user.region = originParts[1];
user.continent = originParts[2];
}
};
CTXValueGenerationBlock *encodeOrigin = ^id(id object){
return [[user.origin, user.region, user.continent] componentsJoinedByString:@","];
};
[mapper addMappings:@{@"title":CTXProperty(title),
@"sector":CTXProperty(sector),
@"hours":CTXProperty(hours),
}
forClass:[Job class]];
[mapper addMappings:@{@"firstName":CTXProperty(firstName),
@"lastName":CTXProperty(lastName),
@"job":CTXClass(job, [Job class]),
@"avatarURL":CTXBlock(profileURL, encodeURL, decodeURL),
@"origin":CTXGenerationConsumerBlock(encodeOrigin, decodeOrigin)
}
forClass:[User class]];
User *user = [mapper createObjectWithClass:[User class] fromDictionary:dictionary];
CTXPropertyMapper 在内部使用默认对象初始化器,但某些技术(如 CoreData)需要自定义初始化器。为了支持这一点,您可以使用自己的自定义工厂实现 CTXPropertyMapperModelFactoryProtocol
协议。该工厂接收类类型和一个字典,以提高灵活性,允许使用已创建的模型,或从本地存储获取模型实例。
@interface CoreDataModelFactory : NSObject<CTXPropertyMapperModelFactoryProtocol>
- (instancetype)initWithContext:(NSManagedObjectContext *)context;
@end
@implementation CoreDataModelFactory
- (id)instanceForClass:(Class)class withDictionary:(NSDictionary *)dictionary
{
NSEntityDescription *entity = [NSEntityDescription entityForName:[class description]] inManagedObjectContext:self.context];
return [[NSManagedObjec alloc] initWithEntity:entity insertIntoManagedObjectContext:self.context];
}
@end
现在只是创建一个 CTXPropertyMapper 实例,并用您的自定义模型工厂进行初始化。
CTXPropertyMapper *mapper = [[CTXPropertyMapper alloc] initWithModelFactory:[[CoreDataModelFactory alloc] init]];
CTXPropertyMapper 非常强大灵活,但不能解决所有问题。有时需要对编码或解码对象的最终版本进行改进。为了给开发者提供更多控制,我们引入了一个最终步骤钩子,即在对象由映射器返回之前,可以访问映射器的完整状态,且会运行。
[mapper setFinalMappingDecoder:^(NSDictionary *input, User *object){
NSLog(@"[Warning] User non mapped keys %@", input);
} forClass:[User class] withOption:CTXPropertyMapperFinalMappingDecoderOptionExcludeAlreadyMappedKeys];
[mapper setFinalMappingEncoder:^(NSMutableDictionary *output, User *object){
NSString *fullName = [NSString stringWithFormat:@"%@ %@", object.firstName, object.lastName];
[output setValue:fullName forKey:@"fullName"];
} forClass:[User class]];
通常客户模型的结构与服务器相同。为了避免重复代码,CTXPropertyMapper 支持自动创建模型。
CTXPropertyMapper *mapper = [[CTXPropertyMapper alloc] init]];
[mapper addMappings:[CTXPropertyMapper generateMappingsFromClass:[Job class]]
forClass:[Job class]];
我们使用一些 Objective-C 运行时调用创建有效的映射器,忽略指针地址、块、选择器等。当前支持的属性
映射生成不考虑通过协议继承的属性,或通过运行时动态创建的属性,因此请谨慎使用。
为了支持 keyPath
映射,CTXPropertyMapper 会将包含点(.)的所有键名称视为 keyPaths,并且不支持原本就包含点的键。
如果您的本地模型与远程模型共享属性名,但您不希望像自动映射那样映射整个对象,则可以使用方法 + (NSDictionary *)generateMappingsWithKeys:(NSArray *)keys
,传入要映射的属性的数组。
CTXPropertyMapper *mapper = [[CTXPropertyMapper alloc] init]];
[mapper addMappings:[CTXPropertyMapper generateMappingsWithKeys:@[@"title", @"sector"]]
forClass:[Job class]];
您可以为映射添加验证。
[mapper addMappings:[CTXPropertyMapper generateMappingsFromClass:[Job class]]
forClass:[Job class]];
如果需要,您可以创建自己的验证,通过查看类别 CTXPropertyDescriptor+Validators(h,m)
来获取灵感。
CTXPropertyMapper 在 MIT 许可证下发布。有关详细信息,请参阅 LICENSE 文件。