我可以混合使用 RKEntityMapping 和 RKObjectMapping

Posted

技术标签:

【中文标题】我可以混合使用 RKEntityMapping 和 RKObjectMapping【英文标题】:Can I mix RKEntityMapping and RKObjectMapping 【发布时间】:2014-05-07 15:45:29 【问题描述】:

他们似乎在这里做了一个解决方法: Relationship between RKObjectMapping and RKEntityMapping

我可以在不采取任何解决方法的情况下混合它们吗? 谢谢。

编辑:在这里我添加了一个示例 JSON。我想在 Core Data 中存储的是两个实体 Region,但没有关于 resultCode 或 resultDescription。这就是我问我是否可以混合它们的原因。

"resultCode": 0, "resultDescription": "OK", "resultContent": [ "region_id": 0, "description": "USA" , "region_id": 1, "description": "Europe" ]

【问题讨论】:

如何混合它们 - 提供 JSON 和您的数据对象的详细信息 你好 Wain,请参阅说明。 你的问题的答案是什么? 【参考方案1】:

当我使用旧版 API 时,我自己也遇到了同样的问题。以下是我的解决方案:

响应json:


    "resultCode": 0,
    "resultDescription": "OK",
    "resultContent": [
        
            "region_id": 0,
            "description": "USA"
        ,
        
            "region_id": 1,
            "description": "Europe"
        
    ]

这个问题有两种解决方案:

解决方案 #1:使用带有 @"" 键路径的对象映射

假设您已经拥有属性名称与 json 键名称相同的托管类 Region

@interface Response 
@property (nonatomic) NSInteger resultCode;
@property (nonatomic, strong) NSString resultDescription;
@end 


@implementation Response
// Make it KV compliant 
@end

映射

// Response map
RKObjectMapping*  resMap = [RKObjectMapping mappingForClass: [Response class]];
[resMap addAttributeMappingsFromArray: @[@"resultCode", @"resultDescription"]];

responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:resMap method: RKRequestMethodAny pathPattern:@"" keyPath:@"" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];

// Region map
RKEntityMapping* mapping  =  [RKEntityMapping mappingForEntityForName: name inManagedObjectStore:[[RKObjectManager sharedManager] managedObjectStore]];

[mapping addAttributeMappingsFromArray: @[@"region_id", @"description"]];
descriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping method: RKRequestMethodAny pathPattern:@"/api/regions" keyPath:@"resultContent" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];

得到结果

Response* res = [[mappingResult dictionary] objectForKey:[NSNull null]];
NSArray* reg =  [[mappingResult dictionary] objectForKey:@"resultContent"];

解决方案 #2:使用序列化

您可以注册您的 RKSerialization 实现

[RKMIMETypeSerialization registerClass:[NKJsonSerialization class]
                               forMIMEType:RKMIMETypeJSON];

在您的序列化实现中,您可以检查响应是否为错误响应并创建一个NSError 对象,然后将其发送回 Restkit。

@implementation NKJsonSerialization
+ (id)objectFromData:(NSData *)data error:(NSError **)error

    NSError* serializingError = nil;
    NSMutableDictionary*  jsonObject = [NSJSONSerialization
                                        JSONObjectWithData:data
                                        options:NSJSONReadingMutableContainers
                                        error:&serializingError];

    // Process if there is no error
    if (!serializingError)
    
        NSString* resCodeStr = [jsonObject objectForKey:@"resultCode"];
        
        if ([resCodeStr intValue] != 0) 
        
            // Create your NSError for your domain, contain information about response err
            serializingError == <#new created error#>
            jsonObject = nil;
        else
            [jsonObject removeObjectForKey: @"resultCode"];
            [jsonObject removeObjectForKey: @"resultDescription"];
            serializingError = nil;
        
    
    
    *error = serializingError;
    return jsonObject;

如果您的响应包含错误代码,在您的请求回调中,RestKit 将返回一个 NSError 对象,其中底层错误是您刚刚在序列化期间创建的错误。

此解决方案的美妙之处在于您不必关心映射响应状态。错误的响应将(应该)作为 NSError 处理。

如果 json 对象在顶层包含数据对象(键路径 @""),您仍然可以使用映射来获取它而不会发生键冲突,就像在解决方案 #1 中发生的那样。

【讨论】:

/api/region.它来自哪里 它应该是你的请求路径。【参考方案2】:

我不确定您到底在问什么。RKEntityMapping 用于映射到 Core Data 实体,而 RKObjectMapping 仅用于映射到常规对象表示。所以也许问题是,您是否使用 Core Data?

【讨论】:

您好 EAB,请参阅说明。【参考方案3】:

在这种情况下,您不需要混合它们。创建响应描述符时,您将键路径设置为 resultContent 并仅使用实体映射。

可以通过某些方式混合映射类型,但这通常需要根据具体情况进行考虑。通常,您会使用多个响应描述符来保持映射分离,然后将结果合并为后处理。

【讨论】:

好主意,这也是一种可能,但据我了解,我还需要创建一个动态映射来捕获错误。 您的问题显示为您不想要错误信息。您可以使用动态映射或 2 个响应描述符。【参考方案4】:

您需要为 RKObjectMapping 和 RKEntityMapping 对象定义两个单独的描述符,在您的情况下为 StatusMapping 和 RegionMapping 然后将它们添加到 ObjectManager,它就像魅力一样工作(我给你示例代码和类来实现这一点):

像这样定义 ResponseStatus 类:

//...

ResponseStatus.h

//...

@interface ResponseStatus : NSObject

@property (nonatomic) BOOL resultCode;
@property (nonatomic, strong) NSString *resultDescription;

+ (RKObjectMapping *)rkObjectMappingForResponse:(BOOL)includeAll;
+ (RKObjectMapping *)rkObjectMappingForRequest:(BOOL)includeAll;
+ (NSDictionary *)rkAttributeMappingsDictionary:(BOOL)request includeAll:(BOOL)includeAll;

@end

//...

ResponseStatus.m

//...

@implementation ResponseStatus

    + (RKObjectMapping *)rkObjectMappingForResponse:(BOOL)includeAll 
        RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[ResponseStatus class]];
        [mapping addAttributeMappingsFromDictionary:[self rkAttributeMappingsDictionary:NO includeAll:includeAll]];

        if (includeAll) 
        

        return mapping;
    

    + (RKObjectMapping *)rkObjectMappingForRequest:(BOOL)includeAll 
        RKObjectMapping *mapping = [RKObjectMapping requestMapping];
        [mapping addAttributeMappingsFromDictionary:[self rkAttributeMappingsDictionary:YES includeAll:includeAll]];

        if (includeAll) 
        

        return mapping;
    

    + (NSDictionary *)rkAttributeMappingsDictionary:(BOOL)request includeAll:(BOOL)includeAll 
        NSMutableDictionary *dic = [NSMutableDictionary dictionary];

        if (includeAll) 
            [dic addEntriesFromDictionary:@
                 @"resultCode": @"resultCode",
                 @"resultDescription": @"resultDescription",
             ];
        

        return dic;
    

    @end

为您的 ResponseStatus(结果)定义描述符映射

NSIndexSet *statusCodes = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful); // Anything in 2xx

RKResponseDescriptor *statusResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:[ResponseStatus rkObjectMappingForResponse:YES] method:RKRequestMethodAny pathPattern:nil keyPath:@"" statusCodes:statusCodes];

为你的 RKEntityMapping 定义一个描述符映射

RKResponseDescriptor *gameResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:[Game rkEntityMappingForResponse:YES] method:RKRequestMethodAny pathPattern:nil keyPath:@"games" statusCodes:statusCodes];

向 objectManager 添加响应描述符

[objectManager addResponseDescriptorsFromArray:@[gameResponseDescriptor, statusResponseDescriptor]];

这是处理映射结果的方法

RKObjectRequestOperation *operation = [objectManager managedObjectRequestOperationWithRequest:requestObject managedObjectContext:managedObjectContext success: ^(RKObjectRequestOperation *operation, RKMappingResult *result) 

    if ([RKUtils isResponseStatusError:[result array]]) 
         
         //..       
        

         failure: ^(RKObjectRequestOperation *operation, NSError *error) 
        
        NSLog(@"Failed with error: %@", [error localizedDescription]);  
        ];


+ (BOOL)isResponseStatusError:(NSArray *)itemsList 
    if ([itemsList count] != 1) 
        return NO;
    

    id object = itemsList[0];

    if ([object isKindOfClass:[ResponseStatus class]]) 
        ResponseStatus *responseStatus = object;
        if (!responseStatus.resultCode) 
            NSLog(@"Error : %@", responseStatus.message);
            return YES;
        
    

    return NO;

并进行 REST 调用,希望对您有所帮助。

【讨论】:

你如何处理映射结果? 您不应该假设ResponseStatus 将作为数组中的第一个对象出现。经过快速搜索,我认为这应该是处理结果的更正确方法:[[result dictionary] objectForKey:[NSNull null]]@"" 处的根对象将设置为 NSNull 键(已测试)【参考方案5】:

好的,经过几次测试,我意识到RestKit将RKEntityMapping保存在核心数据中,而不是RKObjectMapping。它完美地工作。我喜欢 RestKit :)

【讨论】:

您介意分享您的映射代码吗?有人可能会觉得它有帮助 你告诉我们的一样。只是不要做任何事情。如果您想将它们保存在核心数据中,请声明 RKObjectMapping;如果您不想保存它们,请声明 RKEntityMapping。 RestKit 很棒。

以上是关于我可以混合使用 RKEntityMapping 和 RKObjectMapping的主要内容,如果未能解决你的问题,请参考以下文章

RestKit 在 RKEntityMapping 行崩溃

通过主键与 RKObjectMapping 和 RKEntityMapping 的关系

RKEntityMapping JSON 数组 RKResponseDescriptor 键路径问题可能

在 RestKit 上使用 DynamicMapping 和 EntityMapping

RESKit:创建了重复的对象

是否可以同时使用混合和深度测试?