美文网首页
JSONModel源码解析

JSONModel源码解析

作者: 点滴86 | 来源:发表于2018-08-13 18:14 被阅读36次

1 使用方法
对JSONModel 做一个简单的封装
定义一个基础模型DMBaseModel继承自JSONModel,对初始化方法做一个简单的封装,以及将属性设置为可选(Optional)
DMBaseModel.h 文件如下

@interface DMBaseModel : JSONModel

- (instancetype)initWithDictionary:(NSDictionary *)dict;

- (instancetype)initWithData:(NSData *)data;

- (instancetype)initWithString:(NSString *)string;

@end

DMBaseModel.m文件如下

#import "DMBaseModel.h"

@implementation DMBaseModel

+ (BOOL)propertyIsOptional:(NSString *)propertyName
{
    return YES;
}

- (instancetype)initWithDictionary:(NSDictionary *)dict
{
    return [self initWithDictionary:dict error:nil];
}

- (instancetype)initWithData:(NSData *)data
{
    return [self initWithData:data error:nil];
}

- (instancetype)initWithString:(NSString *)string
{
    return [self initWithString:string error:nil];
}

@end

定义一个模型继承自DMBaseModel
DMProductModel.h文件如下

#import "DMBaseModel.h"

@protocol DMProductModel

@end

@interface DMProductModel : DMBaseModel

/** 商品Id */
@property (nonatomic, strong) NSString *productId;

/** 商品名称 */
@property (nonatomic, strong) NSString *productName;

/** 商品售价 */
@property (nonatomic, strong) NSNumber *productSalePrice;

/** 测试结构体类型 */
@property (nonatomic, assign) CGPoint productPoint;

@end

DMProductModel.m文件如下,定义了转换属性名称

#import "DMProductModel.h"

@implementation DMProductModel

+ (JSONKeyMapper *)keyMapper
{
    NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
    
    /** params字典中setObject:forKey:
     *  object值为JSON中的key值
     *  key值为模型中的属性值
     */
    [params setObject:@"salePrice" forKey:@"productSalePrice"];
    
    return [[JSONKeyMapper alloc] initWithModelToJSONDictionary:params];
}

/** JSON解析时忽略的属性 */
+ (BOOL)propertyIsIgnored:(NSString *)propertyName
{
    if ([propertyName isEqualToString:@"productPoint"]) {
        return YES;
    }
    
    return NO;
}

@end

字典中的key值为salePrice时,也可以转换如下

NSMutableDictionary *productDict = [[NSMutableDictionary alloc] init];
    [productDict setObject:[NSNumber numberWithInteger:1000001] forKey:@"productId"];
    [productDict setObject:@"卡地亚山度士自动机械腕表" forKey:@"productName"];
    [productDict setObject:[NSNumber numberWithDouble:36999.99] forKey:@"salePrice"];
   /*
   CGPoint tempPoint = CGPointMake(100, 100);
   [productDict setObject:[NSValue valueWithCGPoint:tempPoint] forKey:@"productPoint"];
    // JSONModel 不能解析NSValue,因为NSValue不是JSON允许的格式
    */
    DMProductModel *productOne = [[DMProductModel alloc] initWithDictionary:productDict];

模型嵌套时

#import "DMBaseModel.h"
#import "DMProductModel.h"
#import "DMImageInfoModel.h"

@interface DMUserModel : DMBaseModel

/** 用户Id */
@property (nonatomic, strong) NSString *userId;

/** 用户名称 */
@property (nonatomic, strong) NSString *userName;

/** 用户年龄 */
@property (nonatomic, assign) NSInteger userAge;

/** 喜欢商品数组 */
@property (nonatomic, strong) NSArray<DMProductModel> *loveProductsArray;

/** 用户头像 */
@property (nonatomic, strong) DMImageInfoModel *headIcon;

@end

对DMProductModel 模型嵌套,需要声明 DMProductModel协议,参考DMProductModel.h

@protocol DMProductModel

@end

转化如下

NSMutableDictionary *userDict = [[NSMutableDictionary alloc] init];
    [userDict setObject:@"DM000001" forKey:@"userId"];
    [userDict setObject:@"JackZ86" forKey:@"userName"];
    [userDict setObject:[NSNumber numberWithInteger:18] forKey:@"userAge"];
    NSMutableArray *productArray = [[NSMutableArray alloc] init];
    {
        NSMutableDictionary *productDict = [[NSMutableDictionary alloc] init];
        [productDict setObject:@"000001" forKey:@"productId"];
        [productDict setObject:@"卡地亚山度士自动机械腕表" forKey:@"productName"];
        [productDict setObject:[NSNumber numberWithDouble:36999.99] forKey:@"salePrice"];
        [productArray addObject:productDict];
    }
    {
        NSMutableDictionary *productDict = [[NSMutableDictionary alloc] init];
        [productDict setObject:@"000002" forKey:@"productId"];
        [productDict setObject:@"LV手提包" forKey:@"productName"];
        [productDict setObject:[NSNumber numberWithDouble:66.66] forKey:@"salePrice"];
        [productArray addObject:productDict];
    }
    {
        NSMutableDictionary *productDict = [[NSMutableDictionary alloc] init];
        [productDict setObject:[NSNumber numberWithInteger:100003] forKey:@"productId"];
        [productDict setObject:@"匡威经典鞋" forKey:@"productName"];
        [productDict setObject:[NSNumber numberWithDouble:888.88] forKey:@"salePrice"];
        [productArray addObject:productDict];
    }
    [userDict setObject:productArray forKey:@"loveProductsArray"];
    
    {
        NSMutableDictionary *imgInfoDict = [[NSMutableDictionary alloc] init];
        [imgInfoDict setObject:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1531977670688&di=e6b8cbb146159c0ac82ccb52013ada79&imgtype=0&src=http%3A%2F%2Fimg4.duitang.com%2Fuploads%2Fblog%2F201309%2F15%2F20130915195908_UurWc.thumb.700_0.jpeg" forKey:@"imgUrl"];
        [imgInfoDict setObject:[NSNumber numberWithInteger:375] forKey:@"imgWidth"];
        [imgInfoDict setObject:[NSNumber numberWithInteger:200] forKey:@"imgHeight"];
        [userDict setObject:imgInfoDict forKey:@"headIcon"];
    }
    
    DMUserModel *userModel = [[DMUserModel alloc] initWithDictionary:userDict];

2 源码解析
初始化方法

- (instancetype)initWithString:(NSString *)string error:(JSONModelError **)err;
- (instancetype)initWithString:(NSString *)string usingEncoding:(NSStringEncoding)encoding error:(JSONModelError **)err;
- (instancetype)initWithData:(NSData *)data error:(NSError **)error;
- (instancetype)initWithDictionary:(NSDictionary *)dict error:(NSError **)err;

前三种方法实现

- (id)initWithString:(NSString *)string error:(JSONModelError **)err
{
    JSONModelError *initError = nil;
    id objModel = [self initWithString:string usingEncoding:NSUTF8StringEncoding error:&initError];
    if (initError && err) {
      *err = initError;
    }
    
    return objModel;
}

- (id)initWithString:(NSString *)string usingEncoding:(NSStringEncoding)encoding error:(JSONModelError **)err
{
    //check for nil input
    if (!string) {
        if (err) {
            *err = [JSONModelError errorInputIsNil];
        }
        
        return nil;
    }

    JSONModelError *initError = nil;
    id objModel = [self initWithData:[string dataUsingEncoding:encoding] error:&initError];
    if (initError && err) {
        *err = initError;
    }
    
    return objModel;
}

- (instancetype)initWithData:(NSData *)data error:(NSError *__autoreleasing *)err
{
    //check for nil input
    if (!data) {
        if (err) {
            *err = [JSONModelError errorInputIsNil];
        }
        
        return nil;
    }
    //read the json
    JSONModelError *initError = nil;
    id obj = [NSJSONSerialization JSONObjectWithData:data
                                             options:kNilOptions
                                               error:&initError];

    if (initError) {
        if (err) {
            *err = [JSONModelError errorBadJSON];
        }
        
        return nil;
    }

    //init with dictionary
    id objModel = [self initWithDictionary:obj error:&initError];
    if (initError && err) {
        *err = initError;
    }
    
    return objModel;
}

通过代码可以知道最终都会调用到- (id)initWithDictionary:(NSDictionary *)dict error:(NSError **)err初始化方法
看- (id)initWithDictionary:(NSDictionary *)dict error:(NSError **)err方法之前先看一下+ (void)load方法
2.1 load方法源码

+ (void)load
{
    static dispatch_once_t once;
    dispatch_once(&once, ^{
        // initialize all class static objects,
        // which are common for ALL JSONModel subclasses

        @autoreleasepool {
            // 兼容的对象类型
            allowedJSONTypes = @[
                [NSString class],
                [NSNumber class],
                [NSDecimalNumber class],
                [NSArray class],
                [NSDictionary class],
                [NSNull class], //immutable JSON classes
                [NSMutableString class],
                [NSMutableArray class],
                [NSMutableDictionary class] //mutable JSON classes
            ];

            // 兼容的基本类型
            allowedPrimitiveTypes = @[
                @"BOOL",
                @"float",
                @"int",
                @"long",
                @"double",
                @"short",
                //and some famous aliases
                @"NSInteger",
                @"NSUInteger",
                @"Block"
            ];

            // 转换器
            valueTransformer = [[JSONValueTransformer alloc] init];

            // This is quite strange, but I found the test isSubclassOfClass: (line ~291) to fail if using [JSONModel class].
            // somewhat related: https://stackoverflow.com/questions/6524165/nsclassfromstring-vs-classnamednsstring
            // //; seems to break the unit tests

            // Using NSClassFromString instead of [JSONModel class], as this was breaking unit tests, see below
            //http://stackoverflow.com/questions/21394919/xcode-5-unit-test-seeing-wrong-class
            // 类型
            JSONModelClass = NSClassFromString(NSStringFromClass(self));
        }
    });
}

这个方法主要是对静态变量的赋值,比如兼容的对象类型、兼容的基本类型、转换器、以及Class类型
2.2 初始化- (id)initWithDictionary:(NSDictionary *)dict error:(NSError **)方法源码

- (id)initWithDictionary:(NSDictionary *)dict error:(NSError **)err
{
    //check for nil input
    // 输入dict是否为空
    if (!dict) {
        if (err) {
            *err = [JSONModelError errorInputIsNil];
        }
        
        return nil;
    }
    
    //invalid input, just create empty instance
    // 输入dict是否是字典类型
    if (![dict isKindOfClass:[NSDictionary class]]) {
        if (err) {
            *err = [JSONModelError errorInvalidDataWithMessage:@"Attempt to initialize JSONModel object using initWithDictionary:error: but the dictionary parameter was not an 'NSDictionary'."];
        }
        
        return nil;
    }

    //create a class instance
    // 初始化
    self = [self init];
    if (!self) {
        //super init didn't succeed
        if (err) {
            *err = [JSONModelError errorModelIsInvalid];
        }
        
        return nil;
    }

    //check incoming data structure
    // 检查输入数据是否完整(必选属性集合对应的值都存在)
    if (![self __doesDictionary:dict matchModelWithKeyMapper:self.__keyMapper error:err]) {
        return nil;
    }

    //import the data from a dictionary
    // 通过dict对属性进行赋值
    if (![self __importDictionary:dict withKeyMapper:self.__keyMapper validation:YES error:err]) {
        return nil;
    }

    //run any custom model validation
    // 自定义model的验证
    if (![self validate:err]) {
        return nil;
    }

    //model is valid! yay!
    return self;
}

首先是对输入dict是否为nil,以及是否是字典的检查,然后是对model的初始化,检查输入数据的完整性,再然后才是对属性的赋值,最后对model的验证,返回
2.2.1 init方法源码

- (id)init
{
    self = [super init];
    if (self) {
        //do initial class setup
        [self __setup__];
    }
    return self;
}

- (void)__setup__
{
    //if first instance of this model, generate the property list
    // 获取关联kClassPropertiesKey属性值
    if (!objc_getAssociatedObject(self.class, &kClassPropertiesKey)) {
        [self __inspectProperties];
    }

    //if there's a custom key mapper, store it in the associated object
    // 如果自定义了keyMapper; 将其保存在关联对象kMapperObjectKey中
    id mapper = [[self class] keyMapper];
    if ( mapper && !objc_getAssociatedObject(self.class, &kMapperObjectKey) ) {
        objc_setAssociatedObject(
                                 self.class,
                                 &kMapperObjectKey,
                                 mapper,
                                 OBJC_ASSOCIATION_RETAIN // This is atomic
                                 );
    }
}

关联kClassPropertiesKey属性值以及关联kMapperObjectKey属性值
2.2.1.1 __inspectProperties方法源码

- (void)__inspectProperties
{
    //JMLog(@"Inspect class: %@", [self class]);

    NSMutableDictionary *propertyIndex = [NSMutableDictionary dictionary];

    //temp variables for the loops
    // 获取当前的类名
    Class class = [self class];
    NSScanner *scanner = nil;
    NSString *propertyType = nil;

    // inspect inherited properties up to the JSONModel class
    // 循环条件: 当class 类名是JSONModel 时候跳出循环
    while (class != [JSONModel class]) {
        //JMLog(@"inspecting: %@", NSStringFromClass(class));

        // 属性个数
        unsigned int propertyCount;
        // 获取属性列表 (所有@property 声明的属性)
        objc_property_t *properties = class_copyPropertyList(class, &propertyCount);

        //loop over the class properties
        // 遍历所有的属性
        for (unsigned int i = 0; i < propertyCount; i++) {

            // 每一个属性都封装成JSONModelClassProperty 对象
            JSONModelClassProperty *p = [[JSONModelClassProperty alloc] init];

            //get property name
            // 获取当前属性
            objc_property_t property = properties[i];
            // 获取属性名
            const char *propertyName = property_getName(property);
            p.name = @(propertyName);

            //JMLog(@"property: %@", p.name);

            //get property attributes
            // 获取属性类型
            const char *attrs = property_getAttributes(property);
            NSString *propertyAttributes = @(attrs);
            /** DMProductModel 属性类型 */
            // T@"NSString",&,N,V_productId
            // T@"NSString",&,N,V_productName
            // T@"NSNumber",&,N,V_productSalePrice
            // T{CGPoint=dd},N,V_productPoint
            /** DMProductModel 属性类型 */
            
            /** DMUserModel 属性类型 */
            // T@"NSString",&,N,V_userId
            // T@"NSString",&,N,V_userName
            // Tq,N,V_userAge
            // T@"NSArray<DMProductModel>",&,N,V_loveProductsArray
            // T@"DMImageInfoModel",&,N,V_headIcon
            /** DMUserModel 属性类型 */
            NSArray *attributeItems = [propertyAttributes componentsSeparatedByString:@","];

            //ignore read-only properties
            // 只读属性,不做任何操作
            if ([attributeItems containsObject:@"R"]) {
                continue; //to next property
            }

            //check for 64b BOOLs
            if ([propertyAttributes hasPrefix:@"Tc,"]) {
                //mask BOOLs as structs so they can have custom converters
                p.structName = @"BOOL";
            }

            scanner = [NSScanner scannerWithString: propertyAttributes];

            //JMLog(@"attr: %@", [NSString stringWithCString:attrs encoding:NSUTF8StringEncoding]);
            [scanner scanUpToString:@"T" intoString: nil];
            [scanner scanString:@"T" intoString:nil];

            //check if the property is an instance of a class
            if ([scanner scanString:@"@\"" intoString: &propertyType]) {
                // 属性是对象类型
                [scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@"\"<"]
                                        intoString:&propertyType];

                //JMLog(@"type: %@", propertyClassName);
                p.type = NSClassFromString(propertyType);
                p.isMutable = ([propertyType rangeOfString:@"Mutable"].location != NSNotFound);
                p.isStandardJSONType = [allowedJSONTypes containsObject:p.type];

                //read through the property protocols
                // 获取属性遵守的协议
                while ([scanner scanString:@"<" intoString:NULL]) {

                    NSString* protocolName = nil;

                    [scanner scanUpToString:@">" intoString: &protocolName];

                    if ([protocolName isEqualToString:@"Optional"]) {
                        p.isOptional = YES;
                    } else if([protocolName isEqualToString:@"Index"]) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
                        p.isIndex = YES;
#pragma GCC diagnostic pop

                        objc_setAssociatedObject(
                                                 self.class,
                                                 &kIndexPropertyNameKey,
                                                 p.name,
                                                 OBJC_ASSOCIATION_RETAIN // This is atomic
                                                 );
                    } else if([protocolName isEqualToString:@"Ignore"]) {
                        p = nil;
                    } else {
                        p.protocol = protocolName;
                    }

                    [scanner scanString:@">" intoString:NULL];
                }

            } else if ([scanner scanString:@"{" intoString: &propertyType]) {
                //check if the property is a structure
                /**
                 *  属性是结构体类型
                 *  T{CGPoint=dd},N,V_productPoint 时,structName为CGPoint
                 */
                [scanner scanCharactersFromSet:[NSCharacterSet alphanumericCharacterSet]
                                    intoString:&propertyType];

                p.isStandardJSONType = NO;
                p.structName = propertyType;

            } else {
                //the property must be a primitive
                // 属性是基本类型
                
                //the property contains a primitive data type
                [scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@","]
                                        intoString:&propertyType];

                //get the full name of the primitive type
                /**
                 *  // Tq,N,V_userAge 时 propertyType 时q
                 *  通过转换器中的基本类型字符映射获取基本类型的全名
                 */
                propertyType = valueTransformer.primitivesNames[propertyType];

                // 兼容基本类型时候包含获取的基本类型全名
                if (![allowedPrimitiveTypes containsObject:propertyType]) {

                    //type not allowed - programmer mistaken -> exception
                    @throw [NSException exceptionWithName:@"JSONModelProperty type not allowed"
                                                   reason:[NSString stringWithFormat:@"Property type of %@.%@ is not supported by JSONModel.", self.class, p.name]
                                                 userInfo:nil];
                }

            }

            NSString *nsPropertyName = @(propertyName);
            // 属性是否可选
            if([[self class] propertyIsOptional:nsPropertyName]){
                p.isOptional = YES;
            }

            // 属性是否可以忽略
            if([[self class] propertyIsIgnored:nsPropertyName]){
                p = nil;
            }

            // 属性是集合类型,是否遵守协议
            Class customClass = [[self class] classForCollectionProperty:nsPropertyName];
            if (customClass) {
                p.protocol = NSStringFromClass(customClass);
            }

            //few cases where JSONModel will ignore properties automatically
            if ([propertyType isEqualToString:@"Block"]) {
                p = nil;
            }

            //add the property object to the temp index
            // p不为nil并且字典中不存在p.name对应的值时,将p加入字典
            if (p && ![propertyIndex objectForKey:p.name]) {
                [propertyIndex setValue:p forKey:p.name];
            }

            // generate custom setters and getter
            // 是否有自定义的getter and setter
            if (p) {
                // 将p.name首字母大写
                NSString *name = [p.name stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:[p.name substringToIndex:1].uppercaseString];

                // getter
                SEL getter = NSSelectorFromString([NSString stringWithFormat:@"JSONObjectFor%@", name]);

                if ([self respondsToSelector:getter]) {
                    p.customGetter = getter;
                }

                // setters
                p.customSetters = [NSMutableDictionary new];

                SEL genericSetter = NSSelectorFromString([NSString stringWithFormat:@"set%@WithJSONObject:", name]);

                if ([self respondsToSelector:genericSetter]) {
                    p.customSetters[@"generic"] = [NSValue valueWithBytes:&genericSetter objCType:@encode(SEL)];
                }

                for (Class type in allowedJSONTypes) {
                    NSString *class = NSStringFromClass([JSONValueTransformer classByResolvingClusterClasses:type]);

                    if (p.customSetters[class])
                        continue;

                    SEL setter = NSSelectorFromString([NSString stringWithFormat:@"set%@With%@:", name, class]);

                    if ([self respondsToSelector:setter])
                        p.customSetters[class] = [NSValue valueWithBytes:&setter objCType:@encode(SEL)];
                }
            }
        }

        free(properties);

        //ascend to the super of the class
        //(will do that until it reaches the root class - JSONModel)
        // 将class 指向父类的类型
        class = [class superclass];
    }

    //finally store the property index in the static property index
    // 将获取的属性字典保存到kClassPropertiesKey关联属性中
    objc_setAssociatedObject(
                             self.class,
                             &kClassPropertiesKey,
                             [propertyIndex copy],
                             OBJC_ASSOCIATION_RETAIN // This is atomic
                             );
}

获取当前类直到父类为JSONModel时的所有属性,并将每一个属性包装到JSONModelClassProperty对象中,最后将获取的属性字典保存到kClassPropertiesKey关联属性中
关于属性的type encodings

/** DMUserModel 属性类型 */
// T@"NSString",&,N,V_userId
// T@"NSString",&,N,V_userName
// Tq,N,V_userAge
// T@"NSArray<DMProductModel>",&,N,V_loveProductsArray
// T@"DMImageInfoModel",&,N,V_headIcon
 /** DMUserModel 属性类型 */

像Tq,N,V_userAge这种时,会通过valueTransformer转换判断基本类型是否在兼容的基本类型中, valueTransformer转换的源码

- (id)init
{
    self = [super init];
    if (self) {
        _primitivesNames = @{@"f":@"float",
                             @"i":@"int",
                             @"d":@"double",
                             @"l":@"long",
                             @"c":@"BOOL",
                             @"s":@"short",
                             @"q":@"long",
                             //and some famous aliases of primitive types
                             // BOOL is now "B" on iOS __LP64 builds
                             @"I":@"NSInteger",
                             @"Q":@"NSUInteger",
                             @"B":@"BOOL",
                             @"@?":@"Block"};
    }
    
    return self;
}

2.2.2 - (BOOL)__doesDictionary:(NSDictionary *)dict matchModelWithKeyMapper:(JSONKeyMapper *)keyMapper error:(NSError **)err方法的源码

/**
 * 验证输入字典dict中是否包含所有的必选属性值
 */
- (BOOL)__doesDictionary:(NSDictionary *)dict matchModelWithKeyMapper:(JSONKeyMapper *)keyMapper error:(NSError **)err
{
    //check if all required properties are present
    // 获取字典中所有的key值
    NSArray* incomingKeysArray = [dict allKeys];
    // 获取所有的必选属性
    NSMutableSet* requiredProperties = [self __requiredPropertyNames].mutableCopy;
    NSSet* incomingKeys = [NSSet setWithArray: incomingKeysArray];

    //transform the key names, if necessary
    // 存在自定义的keyMapper, 转换key名
    if (keyMapper || globalKeyMapper) {

        NSMutableSet* transformedIncomingKeys = [NSMutableSet setWithCapacity: requiredProperties.count];
        NSString* transformedName = nil;

        //loop over the required properties list
        // 遍历所有的属性列表
        for (JSONModelClassProperty* property in [self __properties__]) {
            // 获取转化之后的键值
            transformedName = (keyMapper || globalKeyMapper) ? [self __mapString:property.name withKeyMapper:keyMapper] : property.name;

            //check if exists and if so, add to incoming keys
            // 字典dict中是否存在键对应的值
            id value;
            @try {
                value = [dict valueForKeyPath:transformedName];
            }
            @catch (NSException *exception) {
                value = dict[transformedName];
            }

            if (value) {
                // 如果存在将属性名称加入到集合中
                [transformedIncomingKeys addObject: property.name];
            }
        }

        //overwrite the raw incoming list with the mapped key names
        // 赋值
        incomingKeys = transformedIncomingKeys;
    }

    //check for missing input keys
    // 必选属性集合是否是incomingKeys集合的子集
    if (![requiredProperties isSubsetOfSet:incomingKeys]) {

        //get a list of the missing properties
        [requiredProperties minusSet:incomingKeys];

        //not all required properties are in - invalid input
        JMLog(@"Incoming data was invalid [%@ initWithDictionary:]. Keys missing: %@", self.class, requiredProperties);

        if (err) *err = [JSONModelError errorInvalidDataWithMissingKeys:requiredProperties];
        return NO;
    }

    //not needed anymore
    incomingKeys= nil;
    requiredProperties= nil;

    return YES;
}

验证传入的字典中存在所有model必选属性对应的值
2.2.2.1 如果存在自定义的keyMapper,获取转换之后的键值

/**
 * 获取转化之后的键值
 */
- (NSString *)__mapString:(NSString *)string withKeyMapper:(JSONKeyMapper *)keyMapper
{
    if (keyMapper) {
        //custom mapper
        // 存在自定义的JSONKeyMapper, 获取属性名对应到json中的key值
        NSString* mappedName = [keyMapper convertValue:string];
        if (globalKeyMapper && [mappedName isEqualToString: string]) {
            mappedName = [globalKeyMapper convertValue:string];
        }
        string = mappedName;
    } else if (globalKeyMapper) {
        //global keymapper
        string = [globalKeyMapper convertValue:string];
    }

    return string;
}

2.2.3 通过字典赋值方法源码

- (BOOL)__importDictionary:(NSDictionary *)dict withKeyMapper:(JSONKeyMapper *)keyMapper validation:(BOOL)validation error:(NSError **)err
{
    //loop over the incoming keys and set self's properties
    // 遍历属性集合
    for (JSONModelClassProperty* property in [self __properties__]) {

        //convert key name to model keys, if a mapper is provided
        // 转化键值
        NSString* jsonKeyPath = (keyMapper||globalKeyMapper) ? [self __mapString:property.name withKeyMapper:keyMapper] : property.name;
        //JMLog(@"keyPath: %@", jsonKeyPath);

        //general check for data type compliance
        // 从字典中获取键对应的值
        id jsonValue;
        @try {
            jsonValue = [dict valueForKeyPath: jsonKeyPath];
        }
        @catch (NSException *exception) {
            jsonValue = dict[jsonKeyPath];
        }

        //check for Optional properties
        // 获取到的值是否为空
        if (isNull(jsonValue)) {
            //skip this property, continue with next property
            // 获取值为空时,属性为可选,直接跳过,继续下一个
            if (property.isOptional || !validation)
                continue;

            if (err) {
                //null value for required property
                NSString* msg = [NSString stringWithFormat:@"Value of required model key %@ is null", property.name];
                JSONModelError* dataErr = [JSONModelError errorInvalidDataWithMessage:msg];
                *err = [dataErr errorByPrependingKeyPathComponent:property.name];
            }
            
            return NO;
        }

        // 获取值得类型
        Class jsonValueClass = [jsonValue class];
        BOOL isValueOfAllowedType = NO;

        // 获取值得类型是否在兼容类型里面
        for (Class allowedType in allowedJSONTypes) {
            if ( [jsonValueClass isSubclassOfClass: allowedType] ) {
                isValueOfAllowedType = YES;
                break;
            }
        }

        // 不在兼容类型里面
        if (isValueOfAllowedType == NO) {
            //type not allowed
            JMLog(@"Type %@ is not allowed in JSON.", NSStringFromClass(jsonValueClass));

            if (err) {
                NSString* msg = [NSString stringWithFormat:@"Type %@ is not allowed in JSON.", NSStringFromClass(jsonValueClass)];
                JSONModelError* dataErr = [JSONModelError errorInvalidDataWithMessage:msg];
                *err = [dataErr errorByPrependingKeyPathComponent:property.name];
            }
            
            return NO;
        }

        //check if there's matching property in the model
        if (property) {

            // check for custom setter, than the model doesn't need to do any guessing
            // how to read the property's value from JSON
            // 存在自定义方法就直接调用自定义方法
            if ([self __customSetValue:jsonValue forProperty:property]) {
                //skip to next JSON key
                continue;
            };

            // 0) handle primitives
            if (property.type == nil && property.structName==nil) {
                // 处理基本类型 ,如这个
                // Tq,N,V_userAge
                
                //generic setter
                if (jsonValue != [self valueForKey:property.name]) {
                    // 通过KVC赋值
                    [self setValue:jsonValue forKey: property.name];
                }

                //skip directly to the next key
                // 继续下一个
                continue;
            }

            // 0.5) handle nils
            if (isNull(jsonValue)) {
                if ([self valueForKey:property.name] != nil) {
                    [self setValue:nil forKey: property.name];
                }
                continue;
            }


            // 1) check if property is itself a JSONModel
            if ([self __isJSONModelSubClass:property.type]) {
                // 属性是JSONModel的子类,如下面
                // T@"DMImageInfoModel",&,N,V_headIcon

                //initialize the property's model, store it
                // 通过属性类型自身的转换方法转换
                JSONModelError* initErr = nil;
                id value = [[property.type alloc] initWithDictionary: jsonValue error:&initErr];

                if (!value) {
                    //skip this property, continue with next property
                    if (property.isOptional || !validation) continue;

                    // Propagate the error, including the property name as the key-path component
                    if((err != nil) && (initErr != nil)) {
                        *err = [initErr errorByPrependingKeyPathComponent:property.name];
                    }
                    return NO;
                }
                if (![value isEqual:[self valueForKey:property.name]]) {
                    // 通过KVC赋值
                    [self setValue:value forKey: property.name];
                }

                //for clarity, does the same without continue
                // 继续下一个
                continue;

            } else {

                // 2) check if there's a protocol to the property
                //  ) might or not be the case there's a built in transform for it
                // 属性遵守协议
                if (property.protocol) {
                    //JMLog(@"proto: %@", p.protocol);
                    // 转换jsonValue 根据property , 如下面的这个
                    // T@"NSArray<DMProductModel>",&,N,V_loveProductsArray
                    
                    jsonValue = [self __transform:jsonValue forProperty:property error:err];
                    if (!jsonValue) {
                        if ((err != nil) && (*err == nil)) {
                            NSString* msg = [NSString stringWithFormat:@"Failed to transform value, but no error was set during transformation. (%@)", property];
                            JSONModelError* dataErr = [JSONModelError errorInvalidDataWithMessage:msg];
                            *err = [dataErr errorByPrependingKeyPathComponent:property.name];
                        }
                        return NO;
                    }
                }

                // 3.1) handle matching standard JSON types
                if (property.isStandardJSONType && [jsonValue isKindOfClass: property.type]) {
                    // 属性是标准类型 并且 jsonValue 也是属性type类型
                    //mutable properties
                    if (property.isMutable) {
                        jsonValue = [jsonValue mutableCopy];
                    }

                    //set the property value
                    if (![jsonValue isEqual:[self valueForKey:property.name]]) {
                        // 通过KVC赋值
                        [self setValue:jsonValue forKey: property.name];
                    }
                    continue;
                }

                // 3.3) handle values to transform
                if (
                    (![jsonValue isKindOfClass:property.type] && !isNull(jsonValue))
                    ||
                    //the property is mutable
                    property.isMutable
                    ||
                    //custom struct property
                    property.structName
                    ) {
                    // 处理 需要转化的,例如(字符串NSString productId 但是传入的字典中 [productDict setObject:[NSNumber numberWithInteger:1000001] forKey:@"productId"] 是一个NSNumber)
                    // searched around the web how to do this better
                    // but did not find any solution, maybe that's the best idea? (hardly)
                    Class sourceClass = [JSONValueTransformer classByResolvingClusterClasses:[jsonValue class]];

                    //JMLog(@"to type: [%@] from type: [%@] transformer: [%@]", p.type, sourceClass, selectorName);

                    //build a method selector for the property and json object classes
                    NSString* selectorName = [NSString stringWithFormat:@"%@From%@:",
                                              (property.structName? property.structName : property.type), //target name
                                              sourceClass]; //source name
                    SEL selector = NSSelectorFromString(selectorName);

                    //check for custom transformer
                    // 转换器valueTransformer中能否找到对应转换方法
                    BOOL foundCustomTransformer = NO;
                    if ([valueTransformer respondsToSelector:selector]) {
                        foundCustomTransformer = YES;
                    } else {
                        //try for hidden custom transformer
                        selectorName = [NSString stringWithFormat:@"__%@",selectorName];
                        selector = NSSelectorFromString(selectorName);
                        if ([valueTransformer respondsToSelector:selector]) {
                            foundCustomTransformer = YES;
                        }
                    }

                    //check if there's a transformer with that name
                    if (foundCustomTransformer) {

                        //it's OK, believe me...
                        // 进行转换
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
                        //transform the value
                        jsonValue = [valueTransformer performSelector:selector withObject:jsonValue];
#pragma clang diagnostic pop

                        if (![jsonValue isEqual:[self valueForKey:property.name]]) {
                            // KVC赋值
                            [self setValue:jsonValue forKey: property.name];
                        }

                    } else {
                        NSString* msg = [NSString stringWithFormat:@"%@ type not supported for %@.%@", property.type, [self class], property.name];
                        JSONModelError* dataErr = [JSONModelError errorInvalidDataWithTypeMismatch:msg];
                        *err = [dataErr errorByPrependingKeyPathComponent:property.name];
                        return NO;
                    }

                } else {
                    // 3.4) handle "all other" cases (if any)
                    // 其它情况
                    if (![jsonValue isEqual:[self valueForKey:property.name]]) {
                        [self setValue:jsonValue forKey: property.name];
                    }
                }
            }
        }
    }

    return YES;
}

会遍历每一个属性列表,如果字典中存在对应的jsonValue值,会根据jsonValue值进行不同的处理
0)基本类型时通过KVC直接赋值,例如Tq,N,V_userAge
1)属性是JSONModel的子类时,会将jsonValue通过initWithDictionary方法转化为对象,然后通过KVC赋值,例如T@"DMImageInfoModel",&,N,V_headIcon
2)属性遵守协议时,会将jsonValue根据属性来转换,例如T@"NSArray<DMProductModel>",&,N,V_loveProductsArray
2.2.3.1 根据property转换jsonValue源码

/**
 *  转换value 根据property
 */
- (id)__transform:(id)value forProperty:(JSONModelClassProperty *)property error:(NSError **)err
{
    // 协议从NSString转化成类型
    Class protocolClass = NSClassFromString(property.protocol);
    if (!protocolClass) {

        //no other protocols on arrays and dictionaries
        //except JSONModel classes
        if ([value isKindOfClass:[NSArray class]]) {
            @throw [NSException exceptionWithName:@"Bad property protocol declaration"
                                           reason:[NSString stringWithFormat:@"<%@> is not allowed JSONModel property protocol, and not a JSONModel class.", property.protocol]
                                         userInfo:nil];
        }
        return value;
    }

    // 协议类型是JSONModel的子类
    if ([self __isJSONModelSubClass:protocolClass]) {
        //if the protocol is actually a JSONModel class
        
        //check if it's a list of models
        // 属性类型是NSArray的子类
        if ([property.type isSubclassOfClass:[NSArray class]]) {

            // Expecting an array, make sure 'value' is an array
            // 检查value的类型是NSArray的子类
            if(![[value class] isSubclassOfClass:[NSArray class]]) {
                if(err != nil) {
                    NSString* mismatch = [NSString stringWithFormat:@"Property '%@' is declared as NSArray<%@>* but the corresponding JSON value is not a JSON Array.", property.name, property.protocol];
                    JSONModelError* typeErr = [JSONModelError errorInvalidDataWithTypeMismatch:mismatch];
                    *err = [typeErr errorByPrependingKeyPathComponent:property.name];
                }
                return nil;
            }

            //one shot conversion
            JSONModelError* arrayErr = nil;
            value = [[protocolClass class] arrayOfModelsFromDictionaries:value error:&arrayErr];
            if((err != nil) && (arrayErr != nil)) {
                *err = [arrayErr errorByPrependingKeyPathComponent:property.name];
                return nil;
            }
        } else if ([property.type isSubclassOfClass:[NSDictionary class]]) {
            //check if it's a dictionary of models
            // 属性类型是NSDictionary的子类

            // Expecting a dictionary, make sure 'value' is a dictionary
            // 检查value的类型是NSDictionary的子类
            if(![[value class] isSubclassOfClass:[NSDictionary class]]) {
                if(err != nil) {
                    NSString* mismatch = [NSString stringWithFormat:@"Property '%@' is declared as NSDictionary<%@>* but the corresponding JSON value is not a JSON Object.", property.name, property.protocol];
                    JSONModelError* typeErr = [JSONModelError errorInvalidDataWithTypeMismatch:mismatch];
                    *err = [typeErr errorByPrependingKeyPathComponent:property.name];
                }
                return nil;
            }

            NSMutableDictionary* res = [NSMutableDictionary dictionary];

            for (NSString* key in [value allKeys]) {
                JSONModelError* initErr = nil;
                id obj = [[[protocolClass class] alloc] initWithDictionary:value[key] error:&initErr];
                if (obj == nil) {
                    // Propagate the error, including the property name as the key-path component
                    if((err != nil) && (initErr != nil)) {
                        initErr = [initErr errorByPrependingKeyPathComponent:key];
                        *err = [initErr errorByPrependingKeyPathComponent:property.name];
                    }
                    return nil;
                }
                [res setValue:obj forKey:key];
            }
            value = [NSDictionary dictionaryWithDictionary:res];
        }
    }

    return value;
}

3.1)不是JsonModel 对象类型,也不是基本类型时,如果是兼容的标准类型并且jsonValue也是属性对应的类型时直接通过KVC赋值

3.3)处理需要转换的类型
需要特殊转换的类型,是通过valueTransformer来转换的,查找valueTransformer中是否存在对应的转换函数,例如字符串NSString productId 但是传入的字典中 [productDict setObject:[NSNumber numberWithInteger:1000001] forKey:@"productId"] 是一个NSNumber,就会找到valueTransformer的NSStringFromNSNumber函数来转换,转换完成之后通过KVC赋值
3.4) 其它情况
2.2.4 validate方法

- (BOOL)validate:(NSError **)error
{
    return YES;
}

可以通过validate方法来处理model转换特殊错误处理
2.3 数组转化成model

+ (NSMutableArray *)arrayOfModelsFromDictionaries:(NSArray *)array error:(NSError **)err
{
    //bail early
    if (isNull(array))
        return nil;

    //parse dictionaries to objects
    NSMutableArray* list = [NSMutableArray arrayWithCapacity: [array count]];

    for (id d in array) {
        if ([d isKindOfClass:NSDictionary.class]) {
            JSONModelError* initErr = nil;
            id obj = [[self alloc] initWithDictionary:d error:&initErr];
            if (obj == nil) {
                // Propagate the error, including the array index as the key-path component
                if((err != nil) && (initErr != nil)) {
                    NSString* path = [NSString stringWithFormat:@"[%lu]", (unsigned long)list.count];
                    *err = [initErr errorByPrependingKeyPathComponent:path];
                }
                return nil;
            }

            [list addObject: obj];
        } else if ([d isKindOfClass:NSArray.class]) {
            [list addObjectsFromArray:[self arrayOfModelsFromDictionaries:d error:err]];
        } else {
            // This is very bad
        }
    }

    return list;
}

2.4

- (NSString *)toJSONString;
- (NSData *)toJSONData;
- (NSDictionary *)toDictionary;
- (NSDictionary *)toDictionaryWithKeys:(NSArray *)propertyNames;

前三种方法都是通过- (NSDictionary *)toDictionaryWithKeys:(NSArray *)propertyNames方法实现的

//exports the model as a dictionary of JSON compliant objects
- (NSDictionary *)toDictionaryWithKeys:(NSArray *)propertyNames
{
    NSArray* properties = [self __properties__];
    NSMutableDictionary* tempDictionary = [NSMutableDictionary dictionaryWithCapacity:properties.count];

    id value;

    //loop over all properties
    for (JSONModelClassProperty* p in properties) {

        //skip if unwanted
        if (propertyNames != nil && ![propertyNames containsObject:p.name])
            continue;

        //fetch key and value
        NSString* keyPath = (self.__keyMapper||globalKeyMapper) ? [self __mapString:p.name withKeyMapper:self.__keyMapper] : p.name;
        value = [self valueForKey: p.name];

        //JMLog(@"toDictionary[%@]->[%@] = '%@'", p.name, keyPath, value);

        if ([keyPath rangeOfString:@"."].location != NSNotFound) {
            //there are sub-keys, introduce dictionaries for them
            [self __createDictionariesForKeyPath:keyPath inDictionary:&tempDictionary];
        }

        //check for custom getter
        if ([self __customGetValue:&value forProperty:p]) {
            //custom getter, all done
            [tempDictionary setValue:value forKeyPath:keyPath];
            continue;
        }

        //export nil when they are not optional values as JSON null, so that the structure of the exported data
        //is still valid if it's to be imported as a model again
        if (isNull(value)) {

            if (value == nil)
            {
                [tempDictionary removeObjectForKey:keyPath];
            }
            else
            {
                [tempDictionary setValue:[NSNull null] forKeyPath:keyPath];
            }
            continue;
        }

        //check if the property is another model
        if ([value isKindOfClass:JSONModelClass]) {

            //recurse models
            value = [(JSONModel*)value toDictionary];
            [tempDictionary setValue:value forKeyPath: keyPath];

            //for clarity
            continue;

        } else {

            // 1) check for built-in transformation
            if (p.protocol) {
                value = [self __reverseTransform:value forProperty:p];
            }

            // 2) check for standard types OR 2.1) primitives
            if (p.structName==nil && (p.isStandardJSONType || p.type==nil)) {

                //generic get value
                [tempDictionary setValue:value forKeyPath: keyPath];

                continue;
            }

            // 3) try to apply a value transformer
            if (YES) {

                //create selector from the property's class name
                NSString* selectorName = [NSString stringWithFormat:@"%@From%@:", @"JSONObject", p.type?p.type:p.structName];
                SEL selector = NSSelectorFromString(selectorName);

                BOOL foundCustomTransformer = NO;
                if ([valueTransformer respondsToSelector:selector]) {
                    foundCustomTransformer = YES;
                } else {
                    //try for hidden transformer
                    selectorName = [NSString stringWithFormat:@"__%@",selectorName];
                    selector = NSSelectorFromString(selectorName);
                    if ([valueTransformer respondsToSelector:selector]) {
                        foundCustomTransformer = YES;
                    }
                }

                //check if there's a transformer declared
                if (foundCustomTransformer) {

                    //it's OK, believe me...
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
                    value = [valueTransformer performSelector:selector withObject:value];
#pragma clang diagnostic pop

                    [tempDictionary setValue:value forKeyPath: keyPath];

                } else {

                    //in this case most probably a custom property was defined in a model
                    //but no default reverse transformer for it
                    @throw [NSException exceptionWithName:@"Value transformer not found"
                                                   reason:[NSString stringWithFormat:@"[JSONValueTransformer %@] not found", selectorName]
                                                 userInfo:nil];
                    return nil;
                }
            }
        }
    }

    return [tempDictionary copy];
}

理解了如何从json转化成Model,这个只是逆向的一个过程。

相关文章

  • JSONModel源码解析

    1.HEX0-JSONModel源码解析2.叶孤城___ JSONModel源码解析3.JSON转Model内部实...

  • 很好的 JSONModel

    JSON数据---JSONModel解析 JSON数据---JSONModel解析 JSONModel 初始化流程...

  • JSONModel源码解析

    1 使用方法对JSONModel 做一个简单的封装定义一个基础模型DMBaseModel继承自JSONModel,...

  • JSONModel源码解析

    这一系列的源码解析分享到现在已经是第五篇了。这五篇讲解的都是view层的一些流行的iOS开源框架。而从本篇开始开始...

  • jsonmodel源码解析

    http://www.cocoachina.com/ios/20150104/10821.html http://...

  • JSONModel 使用记录

    JSONModel 嵌套解析JSONModel 解析 json 数据时,如果 json 内部有嵌套,如果想要解析则...

  • ios使用JSONModel遇到Crash问题*** Termi

    使用JSONModel第三方库解析从平台获取的数据id jsonmodel= [[NSClassFromStrin...

  • JSONModel的使用以及源码解析

    前言 如今基本都是客户端请求服务器的模式,数据传递方式基本都使用JSON的字符串方式。然而当我们获取了服务器发来的...

  • JSONModel使用「数据转模型」

    准备: JSONModel_下载 快速的解析数据为Model模型,支持层级嵌套Model模型解析,指定类型自动转化...

  • JSONModel使用

    准备: JSONModel_下载 快速的解析数据为Model模型,支持层级嵌套Model模型解析,指定类型自动转化...

网友评论

      本文标题:JSONModel源码解析

      本文链接:https://www.haomeiwen.com/subject/gjoybftx.html