2012-06-24 66 views
5

我正在处理我的第一个RestKit应用程序,并使用CoreData进行本地缓存。在这里我有一个多对多的关系Post < < --- >>分类。这种关系被称为帖子和类别。RestKit多对多映射,如何?

在我的Post JSON调用中,我得到了如下类别的ID:{"post": [...] "categories":[1,4] [...],其中1和4是ID。

我有一个从NSManagedObject继承的自定义Post模型对象,在这我有类别的属性,像这样:

@interface Post : NSManagedObject 

[...] 
@property (nonatomic, retain) NSSet *categories; 
[...] 

我做一个自定义的类模型对象同样的事情。

我的映射,目前看起来是这样的:

RKManagedObjectMapping *categoryMapping = [RKManagedObjectMapping mappingForClass:[Category class] inManagedObjectStore:objectManager.objectStore]; 
categoryMapping.primaryKeyAttribute = @"categoryId"; 
categoryMapping.rootKeyPath = @"categories"; 
[categoryMapping mapKeyPath:@"id" toAttribute:@"categoryId"]; 
[categoryMapping mapKeyPath:@"name" toAttribute:@"name"]; 
[...] 


RKManagedObjectMapping* postMapping = [RKManagedObjectMapping mappingForClass:[Post class] inManagedObjectStore:objectManager.objectStore]; 

postMapping.primaryKeyAttribute = @"postId"; 
postMapping.rootKeyPath = @"post"; 
[postMapping mapKeyPath:@"id" toAttribute:@"postId"]; 
[...] 
[postMapping mapKeyPath:@"created_at" toAttribute:@"createdAt"]; 
[...] 
// Categories many-to-many. 
[postMapping mapKeyPath:@"categories" toRelationship:@"categories" withMapping:categoryMapping]; 

当我运行此我得到的错误:'[<__NSCFNumber 0x6c625e0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key id.'

我真的很感激,如果有人可以给我如何映射这种关系的任何线索。

回答

2

出现错误是因为您的JSON将每个类别作为主键返回,即:NSNumber。但是你的映射需要一个嵌套对象(它将被转换成一个KVC对象)。当你的地图试图访问KVC对象(实际上是一个NSNumber)的属性“id”时,它会像这样崩溃。

我不知道这是否会工作,但你可以尝试这样的事:

RKManagedObjectMapping* categoryMapping = //something 
categoryMapping.primaryKeyAttribute = @"categoryId"; 
[categoryMapping mapKeyPath: @"" toAttribute: @"categoryId"]; 

[..] 

[postMapping mapKeyPath: @"categories" toRelationship: @"categories" withMapping: categoryMapping]; 

通常情况下,你可以使用[RKManagedObjectMapping connectRelationship:withObjectForPrimaryKeyAttribute:]的方法,但我不知道如果这将在多对多的关系中起作用。您可能必须修改源代码才能执行此操作。要查看的代码位于[RKManagedObjectMappingOperation connectRelationship:]内部。如果可能的话,我会建议你改变你的JSON源,以便类别数组是这样的:“categories”:[{“categoryID:1},{”categoryID“:4}]。如果源不是可能改变,您可以修改使用委托方法或RKObjectLoader扩展手动将数据下面我给:

.H

typedef void(^RKObjectLoaderWillMapDataBlock)(id* mappableData); 

@interface RKObjectLoader (Extended) 

@property (nonatomic, copy) RKObjectLoaderWillMapDataBlock onWillMapData; 

@end 

.M

#import "RKObjectLoader+Extended.h" 

#import <objc/runtime.h> 

NSString* kOnWillMapDataKey = @"onWillMapData"; 

@implementation RKObjectLoader (Extended) 

- (RKObjectLoaderWillMapDataBlock) onWillMapData { 
    return objc_getAssociatedObject(self, &kOnWillMapDataKey); 
} 

- (void) setOnWillMapData:(RKObjectLoaderWillMapDataBlock) block { 
    objc_setAssociatedObject(self, &kOnWillMapDataKey, block, OBJC_ASSOCIATION_COPY); 
} 

- (RKObjectMappingResult*)mapResponseWithMappingProvider:(RKObjectMappingProvider*)mappingProvider toObject:(id)targetObject inContext:(RKObjectMappingProviderContext)context error:(NSError**)error { 
    id<RKParser> parser = [[RKParserRegistry sharedRegistry] parserForMIMEType:self.response.MIMEType]; 
    NSAssert1(parser, @"Cannot perform object load without a parser for MIME Type '%@'", self.response.MIMEType); 

    // Check that there is actually content in the response body for mapping. It is possible to get back a 200 response 
    // with the appropriate MIME Type with no content (such as for a successful PUT or DELETE). Make sure we don't generate an error 
    // in these cases 
    id bodyAsString = [self.response bodyAsString]; 
    RKLogTrace(@"bodyAsString: %@", bodyAsString); 
    if (bodyAsString == nil || [[bodyAsString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] length] == 0) { 
     RKLogDebug(@"Mapping attempted on empty response body..."); 
     if (self.targetObject) { 
      return [RKObjectMappingResult mappingResultWithDictionary:[NSDictionary dictionaryWithObject:self.targetObject forKey:@""]]; 
     } 

     return [RKObjectMappingResult mappingResultWithDictionary:[NSDictionary dictionary]]; 
    } 

    id parsedData = [parser objectFromString:bodyAsString error:error]; 
    if (parsedData == nil && error) { 
     return nil; 
    } 

    // Allow the delegate to manipulate the data 
    if ([self.delegate respondsToSelector:@selector(objectLoader:willMapData:)]) { 
     parsedData = AH_AUTORELEASE([parsedData mutableCopy]); 
     [(NSObject<RKObjectLoaderDelegate>*)self.delegate objectLoader:self willMapData:&parsedData]; 
    } 

    if(self.onWillMapData) { 
     parsedData = AH_AUTORELEASE([parsedData mutableCopy]); 
     self.onWillMapData(&parsedData); 
    } 

    RKObjectMapper* mapper = [RKObjectMapper mapperWithObject:parsedData mappingProvider:mappingProvider]; 
    mapper.targetObject = targetObject; 
    mapper.delegate = (id<RKObjectMapperDelegate>)self; 
    mapper.context = context; 
    RKObjectMappingResult* result = [mapper performMapping]; 

    // Log any mapping errors 
    if (mapper.errorCount > 0) { 
     RKLogError(@"Encountered errors during mapping: %@", [[mapper.errors valueForKey:@"localizedDescription"] componentsJoinedByString:@", "]); 
    } 

    // The object mapper will return a nil result if mapping failed 
    if (nil == result) { 
     // TODO: Construct a composite error that wraps up all the other errors. Should probably make it performMapping:&error when we have this? 
     if (error) *error = [mapper.errors lastObject]; 
     return nil; 
    } 

    return result; 
} 

@end 
+0

感谢,决定改变JSON到'“类别”:[{“category_id”:1},{“category_id”:4}]',你知道我应该如何以及何时填充连接表吗?我目前只获取类别。谢谢! – Anders

+0

什么是连接表?这实际上是另一个问题。 –

+0

是的,谢谢。连接表是两个多对多实体之间的表,其中存储了两个表中的主键。实际上,当我加载帖子时,它实际上会填充这个连接表,但是由于某种原因,它还会在我的类别模型中创建新行。很可能会问这个问题的另一个问题。但改变JSON似乎有所帮助,没有错误没有更多。谢谢! – Anders