JSONをKVCで自動的にモデルに回します

16541 ワード

【図1】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

以前モデルを回してこう書いていました
    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;

コード値を実行してください.JSONのコードは数字と文字列が最後にintタイプになる.これでモデルベース類を書くと体力の代わりに生きることができます.コードは次のとおりです.(オープンソースコード)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 SHYJastor

Class 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 pop
                        
                        if ([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 dictionaryValue] forKey:[maps valueForKey:propertyName]];
        }
        else if (value && [value isKindOfClass:[NSArray class]] && ((NSArray *)value).count > 0) {
            id internalValue = [value objectAtIndexCheck:0];
            if (internalValue && [internalValue isKindOfClass:[SHYJastor class]]) {
                NSMutableArray *internalItems = [NSMutableArray array];
                for (id item in value) {
                    [internalItems addObject:[item dictionaryValue]];
                }
                [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:self
                           selector:@selector(didReceiveMemoryWarning)
                               name:UIApplicationDidReceiveMemoryWarningNotification
                             object: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クラスでも修正してください.コードは次のとおりです.
@interface SHYProduct : SHYJastor

@property (nonatomic, assign) int code;
@property (nonatomic, strong) NSString *msg;
@property (nonatomic, strong) NSArray *data;

@end

@interface SHYProductItem : SHYJastor

@property (nonatomic, strong) NSString *title;

@end


@implementation SHYProduct

+ (Class)dataClass//data;       
{
    return [SHYProductItem class];
}

- (void)dealloc
{
    _msg = nil;
    _data = nil;
}

@end

@implementation SHYProductItem

- (void)dealloc
{
    _title = nil;
}

@end

外部用は便利です.コードは次のとおりです.
    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] initWithDictionary:body];

Modelがもっと上手に書けば、data属性をitemsに変更することができます.コードは以下の通りです.
@interface SHYProduct : SHYJastor

@property (nonatomic, assign) int code;
@property (nonatomic, strong) NSString *msg;
@property (nonatomic, strong) NSArray *items;

@end

@interface SHYProductItem : SHYJastor

@property (nonatomic, strong) NSString *title;

@end

@implementation SHYProduct

+ (Class)itemsClass//items;       
{
    return [SHYProductItem class];
}

- (void)dealloc
{
    _msg = nil;
    _items = nil;
}

- (NSDictionary *)mapping
{
    NSMutableDictionary *maps = [NSMutableDictionary dictionaryWithDictionary:[super mapping]];
    [maps setObject:@"data" forKey:@"items"];//        ,        
    return maps;
}


@end

@implementation SHYProductItem

- (void)dealloc
{
    _title = nil;
}

@end

見たでしょう、便利でしょう、体力の仕事はさようなら