Alex Stone
Alex Stone

Reputation: 47328

iPhone RestKit how to load a local JSON file and map it to a core data entity?

I'm building a two way dropbox sync app. I load objects from core data, convert them to JSON and send them over to dropbox. However, when I do sync, I compare a set of local JSON files to the dropbox JSON files. If a conflict is detected, a sync logic is applied. As a result of the sync logic, a remote JSON file may be downloaded and will replace the local JSON file.

So I end up with a bunch of JSON files in a local documents directory.

How can I use RestKit to deserialize local JSON files back into objects using mappings that I have defined ? The RKTwitterCoreData creates a core data entity from a web-based JSON. I'm trying to do the same with a local JSON file.

There are a bunch of loadObjects methods, but all of them appear to work with web calls:

- (RKObjectLoader*)loadObjectsAtResourcePath:(NSString*)resourcePath delegate:(id<RKObjectLoaderDelegate>)delegate;

Thank you !

Upvotes: 7

Views: 4435

Answers (3)

shannoga
shannoga

Reputation: 19869

This is from Rest-Kit docs, Haven't tried it yet but it looks like the start as they use a JSON string.

You can find it here: look at the bottom of the page

NSString* JSONString = @"{ \"name\": \"The name\", \"number\": 12345}";
NSString* MIMEType = @"application/json";
NSError* error = nil;
id<RKParser> parser = [[RKParserRegistry sharedRegistry] parserForMIMEType:MIMEType];
id parsedData = [parser objectFromString:JSONString error:&error];
if (parsedData == nil && error) {
    // Parser error...
}


RKObjectMappingProvider* mappingProvider = [RKObjectManager sharedManager].mappingProvider;
RKObjectMapper* mapper = [RKObjectMapper mapperWithObject:parsedData mappingProvider:mappingProvider];
RKObjectMappingResult* result = [mapper performMapping];
if (result) {
    // Yay! Mapping finished successfully
}

EDIT see rob5408 note about saving the context:

 [[RKObjectManager sharedManager].objectStore.managedObjectContext save:&error];

Upvotes: 6

adriendenat
adriendenat

Reputation: 3475

If you want to have the exact same behaviour as the "default" Restkit function - (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects you can use that to get the precious objects NSArray :

RKObjectMappingResult* result = [mapper performMapping];
NSArray* objects = [result asCollection];

Upvotes: 1

Alex Stone
Alex Stone

Reputation: 47328

Here's what I'm using for Core Data to JSON to Core Data conversion.

  -(void)deserializeFileAtPath:(NSString*)filePath
{
    DLog(@"Deserialize file: %@",filePath);
    NSError* error = nil;
    NSString *stringJSON = [NSString stringWithContentsOfFile:filePath usedEncoding:nil error:&error];
    if(error)
    {
        NSLog(@"Error reading from file: %@", filePath);
    }

    //restore the dictionary, as it was serialized
    NSDictionary* serializationDictionary =  [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:filePath] options:NSJSONReadingMutableContainers error:&error];

    //Here you must ensure that your object mapping exists
    [CoreDataWrapper setupCoreDataObjectMapping];

//top level object within JSON it will have one entity that you really want to deserialize. Without a wrapper, the mapper would not know what the top level entity really is
    CoreDataWrapper* wrapper = [CoreDataWrapper object];

    RKObjectMapper* mapper;
    error = nil;

//this performs deserialization. if you get errors about designated initializer not being called, you have setup a wrong object mapping. You need to define RKManagedObjectMapping for your core data classes
    mapper = [RKObjectMapper mapperWithObject:serializationDictionary 
                              mappingProvider:[RKObjectManager sharedManager].mappingProvider];
    RKObjectMappingResult* result = [mapper performMapping];

    //top level object within wrapper that holds the real payload
    RealCoreDataEntity* realCoreData = [result asObject];
    realCoreData.wrapper = wrapper;

//just in case
    [[wrapper managedObjectContext]save:nil];

//prints what we got back
    DLog(@"%@", realCoreData);

//prints any nested relationships
    for(NestedRelationshipObject* relationshipEntity in realCoreData.relationship)
    {
        DLog(@"Nested entity:%@", relationshipEntity);
    }

}

Here's how to define a nested RestKit object model. When a JSON file of this structure is being deserialized, it will automatically create all the nested relationships for you, and even merge managed object contexts!

+(void)setupCoreDataObjectMapping
{

RKObjectManager *objectManager = [RKObjectManager sharedManager ] ;

// Setup our object mappings    
/*!
 Mapping by entity. Here we are configuring a mapping by targetting a Core Data entity with a specific
 name. This allows us to map back Twitter user objects directly onto NSManagedObject instances --
 there is no backing model class!
 */
//********************************    
RKManagedObjectMapping* nestedRelationshipMapping = [RKManagedObjectMapping mappingForEntityWithName:@"NestedRelationshipObject"];
//UUID determines which objects get updated and which ones get created during the mapping process
nestedRelationshipMapping.primaryKeyAttribute = @"uuid";
[nestedRelationshipMapping mapKeyPathsToAttributes:
 @"IKeepTheseTheSame", @"IKeepTheseTheSame",
 @"AnotherValue",@"AnotherValue",
 //keep adding your attributes
 nil];
[objectManager.mappingProvider addObjectMapping:nestedRelationshipMapping];




//********************************    

RKManagedObjectMapping* mainPayloadMapping = [RKManagedObjectMapping mappingForEntityWithName:@"RealCoreDataEntity"];
mainPayloadMapping.primaryKeyAttribute = @"uuid";
[mainPayloadMapping mapKeyPathsToAttributes:
 @"companyName",@"companyName",
 //keep adding your attributes
 nil];


//this is the main payload. I create all of it's relationships before, and then add them to the mapping.
[mainPayloadMapping mapRelationship:@"relationshipName" withMapping:nestedRelationshipMapping];
[objectManager.mappingProvider addObjectMapping:mainPayloadMapping];


[objectManager.mappingProvider setSerializationMapping:[mainPayloadMapping inverseMapping] forClass:[YourNSManagedObjectSubclass class]];


[objectManager.mappingProvider setMapping:nestedRelationshipMapping forKeyPath:@"mainPayloadToNestedDataRelationshipName"];
[objectManager.mappingProvider setMapping:mainPayloadMapping forKeyPath:@"wrapperToMainPayloadRelationshipName"];


//this is a top level JSON object. It's name will not be identified within the object, but it's relationshipName will be. The result of deserializing this object would be an object that is being wrapped.
RKManagedObjectMapping* wrapperMapping = [RKManagedObjectMapping mappingForClass:[IconFileWrapper class]];
iconWrapperMapping.primaryKeyAttribute = @"uuid";
//    keyPath and attribute names. must be even
[iconWrapperMapping mapKeyPathsToAttributes:@"uuid",@"uuid",nil];
//keep adding your attributes
[iconWrapperMapping mapRelationship:@"relationshipName" withMapping:mainPayloadMapping];

[objectManager.mappingProvider addObjectMapping:wrapperMapping];
[objectManager.mappingProvider setSerializationMapping:[wrapperMapping inverseMapping] forClass:[YourWrapperNSManagedObjectSubclass class]];


}

Upvotes: 0

Related Questions