5

私は最初の RestKit アプリに取り組んでおり、ローカル キャッシュに CoreData を使用しています。その中で、Post <<--->> カテゴリ間に多対多の関係があります。この関係は、投稿およびカテゴリと呼ばれます。

Post JSON 呼び出しで、次のようなカテゴリの ID を取得します{"post": [...] "categories":[1,4] [...]。1 と 4 は ID です。

Postから継承するカスタムモデル オブジェクトがNSManagedObjectあり、その中に次のようなカテゴリのプロパティがあります。

@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.'

この関係をどのようにマッピングするかについて誰かが私に手がかりを与えることができれば、本当に感謝しています.

4

1 に答える 1

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 配列が次のようになることをお勧めします: "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
于 2012-06-25T07:50:46.850 に答える