Mantle源码学习(二)

MTLJSONSerializing

该协议定义了数据转换之间的规则。需要MTLModel子类去实现。

// 该方法提供属性key与JSON的key path之间的对应关系。注意:如果一个key的对应关系没有被该函数返回,则不将参与JSON序列化。
+ (NSDictionary *)JSONKeyPathsByPropertyKey;
// 可选,通过Model的key获取NSValueTransformer,该方法基本被 +(NSValueTransformer *)key+JSONTransformerForKey;方法替代。
+ (NSValueTransformer *)JSONTransformerForKey:(NSString *)key;
// 指定转换的对象的类型。可选
+ (Class)classForParsingJSONDictionary:(NSDictionary *)JSONDictionary;
MTLJSONAdapter

该类属于数据转换的核心类,转换的过程中该函数需要用户手动调用。

// 该函数提供了讲JSON转为对象的功能。
+ (id)modelOfClass:(Class)modelClass fromJSONDictionary:(NSDictionary *)JSONDictionary error:(NSError **)error

该函数的流程:

  • 首先要初始化一个MTLJSONAdapter对象
MTLJSONAdapter *adapter = [[self alloc] initWithModelClass:modelClass];

该初始化的方法中,主要是检查JSONKeyPathsByPropertyKey中定义的合法性,缓存key的对应关系。同时通过以下代码:

_valueTransformersByPropertyKey = [self.class valueTransformersForModelClass:modelClass];

来对每一个key对应的NSValueTransformer(数据解析转换方法)进行缓存,缓存在一个字典中。这些方法有的是通过Model中来。以下几种是Model中实现的转换方式的几个例子,这种函数的格式是:key的name+JSONTransformer:

// NSURL的转换
+ (NSValueTransformer *)HTMLURLJSONTransformer {
    return [NSValueTransformer valueTransformerForName:MTLURLValueTransformerName];
}

// 特定key-value转换。通过一个Map
+ (NSValueTransformer *)stateJSONTransformer {
    return [NSValueTransformer mtl_valueMappingTransformerWithDictionary:@{
                                                                           @"open": @(GHIssueStateOpen),
                                                                           @"closed": @(GHIssueStateClosed)
                                                                           }];
}

//对象的转换(Model中包含Model),MTLRecursiveUserModel为对象类型)
+ (NSValueTransformer *)ownerJSONTransformer {
    return [MTLJSONAdapter dictionaryTransformerWithModelClass:MTLRecursiveUserModel.class];
}

// 数组的转换(Model中包含数组属性),MTLRecursiveUserModel为数组中数据的类型)
+ (NSValueTransformer *)usersJSONTransformer {
    return [MTLJSONAdapter arrayTransformerWithModelClass:MTLRecursiveUserModel.class];
}

其他普通的数据类型的转换可以直接使用默认的NSValueTransformer来进行。无需在Model中进行实现对应的方法。

  • 然后系统开始序列化数据。
    这一步调用以下代码:
[adapter modelFromJSONDictionary:JSONDictionary error:error];

这个函数中,首先要判断Model是否实现了 classForParsingJSONDictionary 这个函数实现,实现了的话就用得讲JSON转换为这个函数中返回的数据类型。

    if ([self.modelClass respondsToSelector:@selector(classForParsingJSONDictionary:)]) {
        Class class = [self.modelClass classForParsingJSONDictionary:JSONDictionary];
        // 处理异常问题
        if (class == nil) {
            if (error != NULL) {
                NSDictionary *userInfo = @{
                    NSLocalizedDescriptionKey: NSLocalizedString(@"Could not parse JSON", @""),
                    NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"No model class could be found to parse the JSON dictionary.", @"")
                };

                *error = [NSError errorWithDomain:MTLJSONAdapterErrorDomain code:MTLJSONAdapterErrorNoClassFound userInfo:userInfo];
            }

            return nil;
        }

// 当该协议方法中返回的类和当前原有的类不相同的时候,首先得检查是否实现了MTLJSONSerializing协议、然后就要重新初始化一个MTLJSONAdapter对象,然后开始转换啦。并且直接就return,后面的代码就不管了。
        if (class != self.modelClass) {
            NSAssert([class conformsToProtocol:@protocol(MTLJSONSerializing)], @"Class %@ returned from +classForParsingJSONDictionary: does not conform to ", class);

            MTLJSONAdapter *otherAdapter = [self JSONAdapterForModelClass:class error:error];

            return [otherAdapter modelFromJSONDictionary:JSONDictionary error:error];
        }
    }
    

如果以上协议没有实现。就开始数据解析了,整个过程如下

NSMutableDictionary *dictionaryValue = [[NSMutableDictionary alloc] initWithCapacity:JSONDictionary.count];

// 遍历所有的key,解析每一个key的数据
    for (NSString *propertyKey in [self.modelClass propertyKeys]) {
        id JSONKeyPaths = self.JSONKeyPathsByPropertyKey[propertyKey];

        if (JSONKeyPaths == nil) continue;

        id value;

        // 获取key对应的value
        if ([JSONKeyPaths isKindOfClass:NSArray.class]) {
            NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];

            for (NSString *keyPath in JSONKeyPaths) {
                BOOL success = NO;
                id value = [JSONDictionary mtl_valueForJSONKeyPath:keyPath success:&success error:error];

                if (!success) return nil;

                if (value != nil) dictionary[keyPath] = value;
            }

            value = dictionary;
        } else {
            BOOL success = NO;
            value = [JSONDictionary mtl_valueForJSONKeyPath:JSONKeyPaths success:&success error:error];

            if (!success) return nil;
        }

        if (value == nil) continue;

        @try {
        
        // 获取key对应的NSValueTransformer,然后使用该NSValueTransformer去转换数据
            NSValueTransformer *transformer = self.valueTransformersByPropertyKey[propertyKey];
            if (transformer != nil) {
                // Map NSNull -> nil for the transformer, and then back for the
                // dictionary we're going to insert into.
                if ([value isEqual:NSNull.null]) value = nil;

                if ([transformer respondsToSelector:@selector(transformedValue:success:error:)]) {
                    id errorHandlingTransformer = (id)transformer;

                    BOOL success = YES;
                    // 对于数组和对象都会重新new一个MTLJSONAdapter,重新执行上面这些步骤,然后对value进行序列化。
                    value = [errorHandlingTransformer transformedValue:value success:&success error:error];

                    if (!success) return nil;
                } else {
                    // 这种情况一般是普通数据类型
                    value = [transformer transformedValue:value];
                }

                if (value == nil) value = NSNull.null;
            }

            dictionaryValue[propertyKey] = value;
        } @catch (NSException *ex) {
            NSLog(@"*** Caught exception %@ parsing JSON key path \"%@\" from: %@", ex, JSONKeyPaths, JSONDictionary);

            // Fail fast in Debug builds.
            #if DEBUG
            @throw ex;
            #else
            if (error != NULL) {
                NSDictionary *userInfo = @{
                    NSLocalizedDescriptionKey: ex.description,
                    NSLocalizedFailureReasonErrorKey: ex.reason,
                    MTLJSONAdapterThrownExceptionErrorKey: ex
                };

                *error = [NSError errorWithDomain:MTLJSONAdapterErrorDomain code:MTLJSONAdapterErrorExceptionThrown userInfo:userInfo];
            }

            return nil;
            #endif
        }
    }

// 使用KVC讲value设置给对应的key, modelWithDictionary是MTLModel中定义的类函数,最终还是会调用构造函数 (instancetype)initWithDictionary:(NSDictionary *)dictionaryValue error:(NSError **)error;讲对应的数据通过KVC设置给对象,这样就完成了序列化功能。

    id model = [self.modelClass modelWithDictionary:dictionaryValue error:error];

以上就详细的讲解了JSON---->Model的过程。其他的复杂功能暂时没有关注

你可能感兴趣的:(Mantle源码学习(二))