用 KVC 自动把 JSON 转 Model

图1和图2是一个接口,code 是在服务器修改或升级等原因导致的;图3是在新用户登录没有数据的情况出现的;是一个接口对应的Model类也是一个;Model类代码如下

@interface SHYProduct : NSObject@property (nonatomic, assign) int code;@property (nonatomic, strong) NSString *msg;@property (nonatomic, strong) NSArray *data;@end@interface SHYProductItem : NSObject@property (nonatomic, strong) NSString *title;@end#import "SHYProduct.h"@implementation SHYProduct- (void)dealloc{_msg = nil;_data = nil;}@end@implementation SHYProductItem- (void)dealloc{_title = nil;}@end之前我们在转Model是这样写的

NSString *json = @"{\&;code\&;:\&;200\&;,\&;msg\&;:\&;\u83b7\u53d6\u6210\u529f\&;,\&;data\&;:[{\&;title\&;:\&;title 3\&;},{\&;title\&;:\&;title 4\&;}]}";NSData *jsonData = [json dataUsingEncoding:NSUTF8StringEncoding];NSDictionary *body = kNSDictionary([NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:nil]);SHYProduct *product = [[SHYProduct alloc] init];product.code = [body intForKey:@"code"];product.msg = [body stringForKey:@"msg"];NSArray *rows = [body arrayForKey:@"data"];NSMutableArray *items = [[NSMutableArray alloc] init];for (id row in rows) {NSDictionary *dictionary = kNSDictionary(row);SHYProductItem *item = [[SHYProductItem alloc] init];item.title = [dictionary stringForKey:@"title"];[items addObject:item];}product.data = items;

这样写没有什么错,唯一的是代码大,体力活;

关于 intForKey 之类的方法请看网络接口协议 JSON 解析 Crash 的哪些事如果我们不想做这个体力活;有没有办法呢;办法是有一个的,用KVC +Runtime;在用这个之前我们要确认一点用KVC code字段有数字和字符串能不能转成int类型;是否可以测试一下就知道;代码如下:

NSString *json = @"{\&;code\&;:\&;200\&;,\&;msg\&;:\&;\u83b7\u53d6\u6210\u529f\&;,\&;data\&;:[{\&;title\&;:\&;title 3\&;},{\&;title\&;:\&;title 4\&;}]}";NSData *jsonData = [json dataUsingEncoding:NSUTF8StringEncoding];NSDictionary *body = kNSDictionary([NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:nil]);SHYProduct *product = [[SHYProduct alloc] init];[product setValue:[body valueForKey:@"code"] forKey:@"code"];//这里会转成数字,KVC自动转换了[product setValue:[body valueForKey:@"msg"] forKey:@"msg"];NSArray *rows = [body arrayForKey:@"data"];NSMutableArray *items = [[NSMutableArray alloc] init];for (id row in rows) {NSDictionary *dictionary = kNSDictionary(row);SHYProductItem *item = [[SHYProductItem alloc] init];[item setValue:[dictionary valueForKey:@"title"] forKey:@"title"];[items addObject:item];}product.data = items;运行一下code值过去了;JSON的code是数字和字符串最后都能变成int类型;这下好办了写一个Model基类就可以替代体力活了;代码如下:(开源代码https://github.com/elado/jastor)

#import <Foundation/Foundation.h>/*! @class SHYJastor @abstract 把 NSDictionary 转成 model 用的 */@interface SHYJastor : NSObject<NSCoding>/*! @property objectId @abstract 对象的id */@property (nonatomic, copy) NSString *objectId;/*! @method objectWithDictionary: @abstract 指定 NSDictionary 对象转成 model 对象 @param dictionary NSDictionary的对象 @result 返回 model 对象 */+ (id)objectWithDictionary:(NSDictionary *)dictionary;/*! @method initWithDictionary: @abstract 指定 NSDictionary 对象转成 model 对象 @param dictionary NSDictionary的对象 @result 返回 model 对象 */- (id)initWithDictionary:(NSDictionary *)dictionary;/*! @method dictionaryValue @abstract 把对象转成 NSDictionary 对象 @result 返回 NSDictionary 对象 */- (NSMutableDictionary *)dictionaryValue;/*! @method mapping @abstract model 属性 与 NSDictionary 不一至时的映射 */- (NSDictionary *)mapping;@end#import "SHYJastor.h"#import "SHYJastorRuntimeHelper.h"#import "NSArray+SHYUtil.h"#import "NSDictionary+SHYUtil.h"static NSString *idPropertyName = @"id";static NSString *idPropertyNameOnObject = @"objectId";@implementation SHYJastorClass dictionaryClass;Class arrayClass;+ (id)objectWithDictionary:(NSDictionary *)dictionary{id item = [[self alloc] initWithDictionary:dictionary];return item;}- (id)initWithDictionary:(NSDictionary *)dictionary{if (!dictionaryClass)dictionaryClass = [NSDictionary class];if (!arrayClass)arrayClass = [NSArray class];self = [super init];if (self) {NSDictionary *maps = [self mapping];NSArray *propertys = [SHYJastorRuntimeHelper propertyNames:[self class]];for (NSDictionary *property in propertys) {NSString *propertyName = [property stringForKey:@"name"];id key = [maps valueForKey:propertyName];id value = [dictionary valueForKey:key];if (value == [NSNull null] || value == nil) {continue;}if ([SHYJastorRuntimeHelper isPropertyReadOnly:[property stringForKey:@"attributes"]]) {continue;}if ([value isKindOfClass:dictionaryClass]) {Class aClass = NSClassFromString([property stringForKey:@"type"]);if (![aClass isSubclassOfClass:[NSDictionary class]]) {continue;}value = [[aClass alloc] initWithDictionary:value];}else if ([value isKindOfClass:arrayClass]) {NSArray *items = (NSArray *)value;NSMutableArray *objects = [NSMutableArray arrayWithCapacity:[items count]];for (id item in items) {if ([[item class] isSubclassOfClass:dictionaryClass]) {SEL selector = NSSelectorFromString([NSString stringWithFormat:@"%@Class", propertyName]);#pragma clang diagnostic push#pragma clang diagnostic ignored "-Warc-performSelector-leaks"Class aClass = ([[self class] respondsToSelector:selector]) ? [[self class] performSelector:selector] : nil;#pragma clang diagnostic popif ([aClass isSubclassOfClass:[NSDictionary class]]) {[objects addObject:item];}else if ([aClass isSubclassOfClass:[SHYJastor class]]) {SHYJastor *childDTO = [[aClass alloc] initWithDictionary:item];[objects addObject:childDTO];}}else {[objects addObject:item];}}value = objects;}[self setValue:value forKey:propertyName];}id objectId;if ((objectId = [dictionary objectForKey:idPropertyName]) && objectId != [NSNull null]) {if (![objectId isKindOfClass:[NSString class]]) {objectId = [NSString stringWithFormat:@"%@", objectId];}[self setValue:objectId forKey:idPropertyNameOnObject];}}return self;}- (void)dealloc{_objectId = nil;}- (void)encodeWithCoder:(NSCoder *)encoder{[encoder encodeObject:_objectId forKey:idPropertyNameOnObject];NSArray *propertys = [SHYJastorRuntimeHelper propertyNames:[self class]];for (NSDictionary *property in propertys) {NSString *propertyName = [property stringForKey:@"name"];[encoder encodeObject:[self valueForKey:propertyName] forKey:propertyName];}}- (id)initWithCoder:(NSCoder *)decoder{self = [super init];if (self) {[self setValue:[decoder decodeObjectForKey:idPropertyNameOnObject] forKey:idPropertyNameOnObject];NSArray *propertys = [SHYJastorRuntimeHelper propertyNames:[self class]];for (NSDictionary *property in propertys) {NSString *propertyName = [property stringForKey:@"name"];if ([SHYJastorRuntimeHelper isPropertyReadOnly:[property stringForKey:@"attributes"]]) {continue;}id value = [decoder decodeObjectForKey:propertyName];if (value != [NSNull null] && value != nil) {[self setValue:value forKey:propertyName];}}}return self;}- (NSMutableDictionary *)dictionaryValue{NSMutableDictionary *infos = [NSMutableDictionary dictionary];if (_objectId) {[infos setObject:_objectId forKey:idPropertyName];}NSDictionary *maps = [self mapping];NSArray *propertys = [SHYJastorRuntimeHelper propertyNames:[self class]];for (NSDictionary *property in propertys) {NSString *propertyName = [property stringForKey:@"name"];id value = [self valueForKey:propertyName];if (value && [value isKindOfClass:[SHYJastor class]]) {[infos setObject:[value dictionary] forKey:[maps valueForKey:propertyName]];}else if (value && [value isKindOfClass:[NSArray class]] && ((NSArray *)value).count > 0) {id internalValue = [value objectForKeyCheck:0];if (internalValue && [internalValue isKindOfClass:[SHYJastor class]]) {NSMutableArray *internalItems = [NSMutableArray array];for (id item in value) {[internalItems addObject:[item dictionary]];}[infos setObject:internalItems forKey:[maps valueForKey:propertyName]];}else {[infos setObject:value forKey:[maps valueForKey:propertyName]];}}else if (value != nil) {[infos setObject:value forKey:[maps valueForKey:propertyName]];}}return infos;}- (NSDictionary *)mapping{NSArray *properties = [SHYJastorRuntimeHelper propertyNames:[self class]];NSMutableDictionary *maps = [[NSMutableDictionary alloc] initWithCapacity:properties.count];for (NSDictionary *property in properties) {NSString *propertyName = [property stringForKey:@"name"];[maps setObject:propertyName forKey:propertyName];}return maps;}- (NSString *)description{NSMutableDictionary *dictionary = [self dictionaryValue];return [NSString stringWithFormat:@"#<%@: id = %@ %@>", [self class], _objectId, [dictionary description]];}- (BOOL)isEqual:(id)object{if (object == nil || ![object isKindOfClass:[SHYJastor class]]) {return NO;}SHYJastor *model = (SHYJastor *)object;return [_objectId isEqualToString:model.objectId];}@end@interface SHYJastorRuntimeHelper : NSObject+ (BOOL)isPropertyReadOnly:(NSString *)attributes;+ (NSArray *)propertyNames:(__unsafe_unretained Class)aClass;@end#import <objc/runtime.h>#import "SHYJastor.h"#import "SHYJastorRuntimeHelper.h"#import "NSArray+SHYUtil.h"#import "NSDictionary+SHYUtil.h"#include <string.h>static NSMutableDictionary *propertyListByClass;static const char *property_getTypeName(const char *attributes) {char buffer[strlen(attributes) + 1];strncpy(buffer, attributes, sizeof(buffer));char *state = buffer, *attribute;while ((attribute = strsep(&state, ",")) != NULL) {if (attribute[0] == 'T') {size_t len = strlen(attribute);attribute[len – 1] = '\0';static char result[256];strncpy(result, attribute + 3, len – 2);return result;}}return "@";}@implementation SHYJastorRuntimeHelper+ (void)initialize{NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];[notificationCenter addObserver:selfselector:@selector(didReceiveMemoryWarning)name:UIApplicationDidReceiveMemoryWarningNotificationobject:nil];}+ (void)didReceiveMemoryWarning{[propertyListByClass removeAllObjects];}+ (BOOL)isPropertyReadOnly:(NSString *)attributes{NSArray *items = [attributes componentsSeparatedByString:@","];NSString *attribute = [items stringAtIndex:1];return [attribute rangeOfString:@"R"].length > 0;}+ (NSArray *)propertyNames:(__unsafe_unretained Class)aClass{if (aClass == [SHYJastor class]) {return [NSArray array];}if (!propertyListByClass) {propertyListByClass = [[NSMutableDictionary alloc] init];}NSString *className = NSStringFromClass(aClass);NSArray *names = [propertyListByClass arrayForKey:className];if (names) {return names;}NSMutableArray *items = [NSMutableArray array];unsigned int itemCount = 0;objc_property_t *propertys = class_copyPropertyList(aClass, &itemCount);for (unsigned int i = 0; i < itemCount; ++i) {objc_property_t property = propertys[i];const char *name = property_getName(property);const char *attributes = property_getAttributes(property);const char *typeName = property_getTypeName(attributes);NSMutableDictionary *item = [NSMutableDictionary dictionary];[item setObject:[NSString stringWithUTF8String:name] forKey:@"name"];[item setObject:[NSString stringWithUTF8String:attributes] forKey:@"attributes"];[item setObject:[NSString stringWithUTF8String:typeName] forKey:@"type"];[items addObject:item];}free(propertys);[propertyListByClass setObject:items forKey:className];NSArray *array = [SHYJastorRuntimeHelper propertyNames:class_getSuperclass(aClass)];[items addObjectsFromArray:array];return items;}@end用这个基类Model类也要修改一下;代码如下:

开始的时侯,我们就知道,总会有终结。

用 KVC 自动把 JSON 转 Model

相关文章:

你感兴趣的文章:

标签云: