将字典解析为对象的对象映射
使用 CocoaPods
pod 'SFMapping', :git => 'https://github.com/stanfy/SFObjectMapping.git', :tag => '0.0.3'
使用 JSONKit 解析 JSON 到字典很方便,但如何将字典解析到模型呢?
很简单!如下所示
SMBaseObject *parsedObject = [SFMappingCore instanceOfClass:[SMBaseObject class] fromObject:dictionary];
您只需要创建 SMBaseObject 并将字典的键路径映射到属性名称。
在 SFBaseObject.h
中
@interface SFBaseObject : NSObject
@property (nonatomic, strong) NSString * ID;
@end
在您代码中更合适的地方设置 SFBaseObject
的映射,格式为 属性名称
- 键路径
#import "SFMapping.h"
#import "NSObject+SFMapping.h"
+ (void)setupMappingsForModelObjects {
[SFBaseObject setSFMappingInfo:
[SFMapping property:@"ID" toKeyPath:@"id"],
nil];
}
让我们看看不同简单属性的映射
@property (nonatomic, strong) NSString * pString;
@property (nonatomic, strong) NSNumber * pNumber;
@property (nonatomic, assign) BOOL pBoolean;
映射
#import "SFMapping.h"
#import "NSObject+SFMapping.h"
+ (void)setupMappingsForModelObjects {
[SFBaseObject setSFMappingInfo:
[SFMapping property:@"pString" toKeyPath:@"someStringFromDictionary"],
[SFMapping property:@"pNumber" toKeyPath:@"someNumberFromDictionary"],
[SFMapping property:@"pBoolean" toKeyPath:@"someBooleanFromDictionary"],
nil];
}
如果 SFObjectWithArray
有 SFArrayItem
对象的数组
@interface SFObjectWithArray : NSObject
@property (nonatomic, strong) NSMutableArray * mutableArray;
@property (nonatomic, strong) NSMutableArray * immutableArray;
@end
映射
#import "SFMapping.h"
#import "NSObject+SFMapping.h"
+ (void)setupMappingsForModelObjects {
[SFObjectWithArray setSFMappingInfo:
[SFMapping collection:@"mutableArray" classString:@"NSMutableArray"
itemClass:@"SFArrayItem" toKeyPath:@"someMutableArrayFromDictionary"],
[SFMapping collection:@"immutableArray" classString:@"NSArray"
itemClass:@"SFArrayItem" toKeyPath:@"someArrayFromDictionary"],
nil];
}
typedef NS_ENUM(NSInteger , SFUserGender) {
SFUserGenderFemale = 0,
SFUserGenderMale = 1
};
typedef NS_ENUM(NSInteger , SFUserType) {
SFUserTypeNormal= 0,
SFUserTypePremium = 1
};
@interface SFUser : NSObject
@property(nonatomic, assign) SFUserGender gender;
@property(nonatomic, assign) SFUserType userType;
@property(nonatomic, copy) NSString * name;
@end
有两个建议的映射属性的方法。
第一个是使用全局映射器与假类名注册
第二个是从映射定义中直接使用自定义映射器
映射 #1. 使用全局映射器
/*
Register global mapper for all types of SFUserGender
*/
NSDictionary *userGenderDictionary =
@{@"female" : @(SFUserGenderFemale),
@"male" : @(SFUserGenderMale)
};
// Registering mapper for fake class named SFUserGender
[SFMappingCore registerMapper:
[SFBlockBasedMapper mapperWithValueTransformBlock:^id(SFMapping *mapping, id value) {
return value ? userGenderDictionary[value] : nil;
}] forClass:@"SFUserGender"];
[SFUser setSFMappingInfo:
// Here we're specifying this property "class", since we registered global mapper for this class previously
[SFMapping property:@"gender" classString:@"SFUserGender"], // gender in JSON
// ....
nil
];
映射 #2. 使用自定义映射器
NSDictionary *userTypeDictionary =
@{@"premium" : @(SFUserTypePremium),
@"normal" : @(SFUserTypeNormal)
};
[SFUser setSFMappingInfo:
// ,,,
// Here we're specifying local mapper which is used only for this mapping
[SFMapping property:@"userType" keyPath:@"type" valueBlock:^id(SFMapping *mapping, id value) {
return value ? userTypeDictionary[value] : nil;;
}],
nil
];
如果对象有对 SFAnotherObject
的引用
@property (nonatomic, strong) SFAnotherObject * anotherObject;
映射
+ (void)setupMappingsForModelObjects {
[SFBaseObject setSFMappingInfo:
[SFMapping property:@"anotherObject" toKeyPath:@"someObjectKeyPath"],
nil];
}
在这种情况下,映射器将尝试创建 SFAnotherObject
并按照提供的规则应用映射。
如果没有找到 SFAnotherObject
的映射,则将创建空对象 [SFAnotherObject new]
如果找到了映射,它们将正确应用
对于日期解析,我们建议使用 SFDateMapper
类
您可以使用预定义的映射器以及一些通用的日期格式
/*
Format : EEE, dd MMM yyyy HH:mm:ss z
locale : en_US_POSIX
Timezone: UTC
*/
+ (SFDateMapper *)rfc2882DateTimeMapper;
/*
Format : yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'
locale : en_US_POSIX
Timezone: UTC
*/
+ (SFDateMapper *)rfc3339DateTimeMapper;
/*
Format : yyyy-MM-dd'T'HH:mm:ssZZZZZ
locale : en_US_POSIX
Timezone: Defined by format
*/
+ (SFDateMapper *)iso8601DateTimeMapper;
或者,如果您想创建自己的,可以创建自己的映射器来处理您自己的,专有的日期格式。
// Mapper for dates in timestamp(seconds since 1970) format
+ (id<SFMapper>)timestampMapper {
static dispatch_once_t once;
static id<SFMapper> mapper;
dispatch_once(&once, ^{
mapper = [SFBlockBasedMapper mapperWithValueTransformBlock:^id(SFMapping *mapping, id value) {
return [NSDate dateWithTimeIntervalSince1970:[value doubleValue]];
}];
});
return mapper;
}
// Mapper for dates in timestamp("/Date(1302055696487)/") format
+ (id<SFMapper>)dotNetTimestampMapper {
static dispatch_once_t once;
static id<SFMapper> mapper;
dispatch_once(&once, ^{
static NSCharacterSet *characterSet = [NSCharacterSet characterSetWithCharactersInString:@"/Date()/"];
mapper = [SFBlockBasedMapper mapperWithValueTransformBlock:^id(SFMapping *mapping, id value) {
NSString *stringWithTimeStamp = [value stringByTrimmingCharactersInSet:characterSet];
return [NSDate dateWithTimeIntervalSince1970:[stringWithTimeStamp doubleValue]];
}];
});
return mapper;
}
一旦拥有日历映射器,无论是 SFDateMapper
子类,还是一些自定义实现,通常有两种使用方式 - 将其注册为 NSDate
类的全局映射器,或将其设置为要映射的自定义映射器
// Since we know that all dates will be in rfc2882 format,
// We just set global mapper on NSDate class
[SFMappingCore registerMapper:[SFDateMapper rfc2882DateTimeMapper] forClass:@"NSDate"];
[SFActivity setSFMappingInfo:
// Mapping date by using global date mapper
[SFMapping property:@"date"],
// Just in case if we need to map this property differently, we can use custom mapper
//[[SFMapping property:@"date"] applyCustomMapper:[SFDateMapper rfc3339DateTimeMapper]],
nil
];
}