I have a problem with the object mapping from a JSON string to a NSManagedObject. I use RestKit via getObject request and the Core Data integration. And I want to automatically map the JSON response.
1.) I get from a webservice the following response ("A" object):
{
"Bs":[
{ "id" : "abc", "name" : "name 1"},
{ "id" : "def", "name" : "name 2"}
{ "id" : "abc", "name" : "name 1"},
],
"id": "1"
}
2.) This response has ordered values (B Objects), with sometimes the same object ("id" : "abc") more than one time. Further more, the order of the items is important.
3.)
Core-Data has not the support to save multiple relations to the same object, because it used a NSSet (NSOrderedSet). And it ignores all double objects.
Has anyone an idea how I could solve the problem?
My try, which fails:
1.) I insert a new core data table (AB), which:
- has a reference to A
- has a position field
- has a reference to one B, from the A

2.)
I try to map the object with a RKValueTransformer instance.
This iterates over the JSON response for the B instances and create AB objects with the current position. These objects are saved in an NSSet, which return from the custom value transformer
RKValueTransformer *aabbTransformer = [RKBlockValueTransformer valueTransformerWithValidationBlock:^BOOL(__unsafe_unretained Class sourceClass, __unsafe_unretained Class destinationClass) {
return ([sourceClass isSubclassOfClass:[NSArray class]] && [destinationClass isSubclassOfClass:[NSOrderedSet class]]);
} transformationBlock:^BOOL(id inputValue, __autoreleasing id *outputValue, Class outputValueClass, NSError *__autoreleasing *error) {
// Validate the input and output
RKValueTransformerTestInputValueIsKindOfClass(inputValue, [NSArray class], error);
RKValueTransformerTestOutputValueClassIsSubclassOfClass(outputValueClass, [NSOrderedSet class], error);
NSMutableOrderedSet *outputSet = [[NSMutableOrderedSet alloc] init];
NSInteger pos = 1;
for (id b in inputValue) {
// see JSON output at the top
// B instance already exists in core data persistent store
NSString *bid = [b valueForKeyPath:@"id"];
B *b = [B bById:bid];
// create AB instance
AB *ab = [NSEntityDescription ...]
ab.b = b;
ab.position = [NSNumber numberWithInteger:pos];
[outputSet addObject:ab];
pos++;
}
// return for A.abs
*outputValue = [[NSOrderedSet alloc] initWithOrderedSet:outputSet];
return YES;
}];
RKAttributeMapping *aabbMapping = [RKAttributeMapping attributeMappingFromKeyPath:@"bs" toKeyPath:@"abs"];
aabbMapping.valueTransformer = aabbMappingTransformer;
3.) But I get an error: illegal attempt to establish a relationship 'abs' between objects in different contexts But I use always the same context.
If you don't have an better idea, do you have a solution for this problem?
Your proposed model structure is a sensible solution for your duplicate data requirement. The only change you should make there is to ensure that all relationships are double ended (have an inverse) and have appropriate multiplicity. Be sure that the relationship from
BtoABis to-many. The relationship fromAtoABshould also be to-many.The general approach to this is to use multiple response descriptors:
Aobject andABobjects, key path isnilBobjects without any duplication, key path isBsThe response descriptor to create the
Aobject has a nested relationship mapping to create theABobjects with duplicates and order - for this you can not use any identification attributes forABand you need to use the mapping metadata provided by RestKit.Once the objects are created they then need to be connected and that would be done with a foreign key mapping. That means storing a transient attribute on the
ABinstances which contains the id for the appropriateBobject and using that to make the connection.Note that after updates you may have some orphaned objects (because you can't use identification attributes for
AB) in the data store as a result of this manipulation and you should consider creating a fetch request block to purge them out (or do this yourself periodically).