我正在尝试通过RestKit将用户和组映射到CoreData对象,维护两者之间的关系。
用户的JSON类似于
{"users":
[{"fullname": "John Doe", "_id": "1"}
...
]
}
并且组的JSON类似于
{"groups":
[{"name": "John's group", "_id": "a",
"members": ["1", "2"]
...
]
}
我还有相应的核心数据对象User
和Group
,在组和用户之间有一对多的关系映射。 Group
中的关系属性称为members
,名为NSArray
的单独memberIDs
包含所有成员用户的字符串ID数组。
我想在RestKit中完成的是加载这些对象并为我映射关系。加载用户的代码是直接的标准内容,加载组的代码类似于
RKObjectManager* objectManager = [RKObjectManager sharedManager];
RKManagedObjectMapping* groupMapping = [RKManagedObjectMapping mappingForClass:[Group class] inManagedObjectStore:objectManager.objectStore];
groupMapping.primaryKeyAttribute = @"identifier";
[groupMapping mapKeyPath:@"_id" toAttribute:@"identifier"];
[groupMapping mapKeyPath:@"name" toAttribute:@"name"];
[groupMapping mapKeyPath:@"members" toAttribute:@"memberIDs"];
[objectManager.mappingProvider setMapping:groupMapping forKeyPath:@"groups"];
// Create an empty user mapping to handle the relationship mapping
RKManagedObjectMapping* userMapping = [RKManagedObjectMapping mappingForClass:[User class] inManagedObjectStore:objectManager.objectStore];
[groupMapping hasMany:@"members" withMapping:userMapping];
[groupMapping connectRelationship:@"members" withObjectForPrimaryKeyAttribute:@"memberIDs"];
RKObjectRouter* router = objectManager.router;
[router routeClass:[Group class] toResourcePath:@"/rest/groups/:identifier"];
[router routeClass:[Group class] toResourcePath:@"/rest/groups/" forMethod:RKRequestMethodPOST];
// Assume url is properly defined to point to the right path...
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:url usingBlock:^(RKObjectLoader *loader) {}];
当我运行此代码时,我会多次吐出以下警告(似乎每个关系大约两次):
2012-10-24 08:55:10.170 Test[35023:18503] W restkit.core_data.cache:RKEntityByAttributeCache.m:205 Unable to add object with nil value for attribute 'identifier': <User: 0x86f7fc0> (entity: User; id: 0x8652400 <x-coredata:///User/t01A9EDBD-A523-4806-AFC2-9B06873D764E531> ; data: {
fullname = nil;
identifier = nil;
})
奇怪的是,关系设置正确(甚至看了sqlite数据库,一切看起来都很好)但我对RestKit代码中出现明显错误的东西感到不满意,似乎有些东西我在上面的代码中创建的伪用户映射(它是空的,因为在ID数组中没有任何内容映射)。
我尝试了替代方案,例如当我添加密钥路径映射时:
[userMapping mapKeyPath:@"" toAttribute:@"identifier"];
它抱怨,显然是因为关键路径是空的
2012-10-24 09:24:11.735 Test[35399:14003] !! Uncaught exception !!
[<__NSCFString 0xa57f460> valueForUndefinedKey:]: this class is not key value coding-compliant for the key .
如果我尝试使用真正的User
映射
[groupMapping hasMany:@"members" withMapping:[[RKObjectManager sharedManager].mappingProvider objectMappingForKeyPath:@"users"]];
我收到以下错误
2012-10-24 09:52:49.973 Test[35799:18403] !! Uncaught exception !!
[<__NSCFString 0xa56e9a0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key _id.
2012-10-24 09:52:49.973 Test[35799:18403] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<__NSCFString 0xa56e9a0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key _id.'
似乎RestKit试图将memberIDs数组中的ID字符串本身映射到User
对象,这没有任何意义。我见过一些例子,其中关系数组是一个带有键的字典列表(例如名为id
),其中包含对另一个对象的引用ID,例如:
{"groups":
[{"name": "John's group", "_id": "a",
"members": [
{"_id": "1"},
{"_id": "2"}
]
...
]
}
但是我不想以这种方式改变我的JSON(此外,我希望RestKit足够灵活以支持其他方式。)
有谁知道在RestKit中进行这种关系映射的正确方法是什么?
更新
我最后修改了REST接口以发送包含用户对象ID的字典列表(就像上面的最后一个例子一样),并让它工作。仍然不满意设置,但代码现在看起来像
RKObjectManager* objectManager = [RKObjectManager sharedManager];
RKManagedObjectMapping* groupMapping = [RKManagedObjectMapping mappingForClass:[Group class] inManagedObjectStore:objectManager.objectStore];
groupMapping.primaryKeyAttribute = @"identifier";
[groupMapping mapKeyPath:@"_id" toAttribute:@"identifier"];
[groupMapping mapKeyPath:@"name" toAttribute:@"name"];
[groupMapping mapKeyPath:@"members._id" toAttribute:@"memberIDs"];
[objectManager.mappingProvider setMapping:groupMapping forKeyPath:@"groups"];
// Create an empty user mapping to handle the relationship mapping
RKManagedObjectMapping* userMapping = [RKManagedObjectMapping mappingForClass:[User class] inManagedObjectStore:objectManager.objectStore];
userMapping.primaryKeyAttribute = @"identifier";
[userMapping mapKeyPath:@"_id" toAttribute:@"identifier"];
[groupMapping hasMany:@"members" withMapping:userMapping];
[groupMapping connectRelationship:@"members" withObjectForPrimaryKeyAttribute:@"memberIDs"];
RKObjectRouter* router = objectManager.router;
[router routeClass:[Group class] toResourcePath:@"/rest/groups/:identifier"];
[router routeClass:[Group class] toResourcePath:@"/rest/groups/" forMethod:RKRequestMethodPOST];
// Assume url is properly defined to point to the right path...
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:url usingBlock:^(RKObjectLoader *loader) {}];
以防这种情况对其他人有所帮助。我采取的方法可能仍然存在一些问题,但到目前为止一切都很好(甚至处理无序加载,这很好) - 如果对原始问题有答案,我仍然希望听到任何人的意见。< / p>
答案 0 :(得分:2)
找到一种方法使这项工作,并且还意识到我的问题中的更新方法只应该用于嵌套对象,而不是引用对象。这个帖子让我走上正轨:https://groups.google.com/forum/#!msg/restkit/swB1Akv2lTE/mnP2OMSqElwJ(如果你正在处理RestKit中的关系,那值得一读)
假设JSON组仍然看起来像原始的JSON:
{"groups":
[{"name": "John's group", "_id": "a",
"members": ["1", "2"]
...
]
}
还假设用户被映射到关键路径users
(即像[objectManager.mappingProvider setMapping:userMapping forKeyPath:@"users"];
那样),映射关系的正确方法如下所示:
RKObjectManager* objectManager = [RKObjectManager sharedManager];
RKManagedObjectMapping* groupMapping = [RKManagedObjectMapping mappingForClass:[Group class] inManagedObjectStore:objectManager.objectStore];
groupMapping.primaryKeyAttribute = @"identifier";
[groupMapping mapKeyPath:@"_id" toAttribute:@"identifier"];
[groupMapping mapKeyPath:@"name" toAttribute:@"name"];
[groupMapping mapKeyPath:@"members" toAttribute:@"memberIDs"];
[objectManager.mappingProvider setMapping:groupMapping forKeyPath:@"groups"];
[groupMapping mapKeyPath:@"users" toRelationship:@"members" withMapping:[[RKObjectManager sharedManager].mappingProvider objectMappingForKeyPath:@"users"] serialize:NO];
[groupMapping connectRelationship:@"members" withObjectForPrimaryKeyAttribute:@"memberIDs"];
RKObjectRouter* router = objectManager.router;
[router routeClass:[Group class] toResourcePath:@"/rest/groups/:identifier"];
[router routeClass:[Group class] toResourcePath:@"/rest/groups/" forMethod:RKRequestMethodPOST];
// Assume url is properly defined to point to the right path...
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:url usingBlock:^(RKObjectLoader *loader) {}];
当我早些尝试这种方法时,困扰我的是这条线
[groupMapping mapKeyPath:@"users" toRelationship:@"members" withMapping:[[RKObjectManager sharedManager].mappingProvider objectMappingForKeyPath:@"users"] serialize:NO];
其中第一个键路径是指被引用对象的键路径映射(在本例中为users
),而不是被映射的group
对象内关系的关键路径(在这种情况下本来是members
。
通过此更改,所有关系都按预期工作,我很高兴。