Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
@interface NSManagedObject (Serialization)
- (NSDictionary*) toDictionary;
- (void) populateFromDictionary:(NSDictionary*)dict;
+ (NSManagedObject*) createManagedObjectFromDictionary:(NSDictionary*)dict
inContext:(NSManagedObjectContext*)context;
@end
#import "NSManagedObject+Serialization.h"
@implementation NSManagedObject (Serialization)
#define DATE_ATTR_PREFIX @"dAtEaTtr:"
#pragma mark -
#pragma mark Dictionary conversion methods
- (NSDictionary*) toDictionaryWithTraversalHistory:(NSMutableArray*)traversalHistory {
NSArray* attributes = [[[self entity] attributesByName] allKeys];
NSArray* relationships = [[[self entity] relationshipsByName] allKeys];
NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithCapacity:
[attributes count] + [relationships count] + 1];
NSMutableArray *localTraversalHistory = nil;
if (traversalHistory == nil) {
localTraversalHistory = [NSMutableArray arrayWithCapacity:[attributes count] + [relationships count] + 1];
} else {
localTraversalHistory = traversalHistory;
}
[localTraversalHistory addObject:self];
[dict setObject:[[self class] description] forKey:@"class"];
for (NSString* attr in attributes) {
NSObject* value = [self valueForKey:attr];
if (value != nil) {
if ([value isKindOfClass:[NSDate class]]) {
NSTimeInterval date = [(NSDate*)value timeIntervalSinceReferenceDate];
NSString *dateAttr = [NSString stringWithFormat:@"%@%@", DATE_ATTR_PREFIX, attr];
[dict setObject:[NSNumber numberWithDouble:date] forKey:dateAttr];
} else {
[dict setObject:value forKey:attr];
}
}
}
for (NSString* relationship in relationships) {
NSObject* value = [self valueForKey:relationship];
if ([value isKindOfClass:[NSSet class]]) {
// To-many relationship
// The core data set holds a collection of managed objects
NSSet* relatedObjects = (NSSet*) value;
// Our set holds a collection of dictionaries
NSMutableArray* dictSet = [NSMutableArray arrayWithCapacity:[relatedObjects count]];
for (NSManagedObject* relatedObject in relatedObjects) {
if ([localTraversalHistory containsObject:relatedObject] == NO) {
[dictSet addObject:[relatedObject toDictionaryWithTraversalHistory:localTraversalHistory]];
}
}
[dict setObject:[NSArray arrayWithArray:dictSet] forKey:relationship];
}
else if ([value isKindOfClass:[NSManagedObject class]]) {
// To-one relationship
NSManagedObject* relatedObject = (NSManagedObject*) value;
if ([localTraversalHistory containsObject:relatedObject] == NO) {
// Call toDictionary on the referenced object and put the result back into our dictionary.
[dict setObject:[relatedObject toDictionaryWithTraversalHistory:localTraversalHistory] forKey:relationship];
}
}
}
if (traversalHistory == nil) {
[localTraversalHistory removeAllObjects];
}
return dict;
}
- (NSDictionary*) toDictionary {
return [self toDictionaryWithTraversalHistory:nil];
}
+ (id) decodedValueFrom:(id)codedValue forKey:(NSString*)key {
if ([key hasPrefix:DATE_ATTR_PREFIX] == YES) {
// This is a date attribute
NSTimeInterval dateAttr = [(NSNumber*)codedValue doubleValue];
return [NSDate dateWithTimeIntervalSinceReferenceDate:dateAttr];
} else {
// This is an attribute
return codedValue;
}
}
- (void) populateFromDictionary:(NSDictionary*)dict
{
NSManagedObjectContext* context = [self managedObjectContext];
for (NSString* key in dict) {
if ([key isEqualToString:@"class"]) {
continue;
}
NSObject* value = [dict objectForKey:key];
if ([value isKindOfClass:[NSDictionary class]]) {
// This is a to-one relationship
NSManagedObject* relatedObject =
[NSManagedObject createManagedObjectFromDictionary:(NSDictionary*)value
inContext:context];
[self setValue:relatedObject forKey:key];
}
else if ([value isKindOfClass:[NSArray class]]) {
// This is a to-many relationship
NSArray* relatedObjectDictionaries = (NSArray*) value;
// Get a proxy set that represents the relationship, and add related objects to it.
// (Note: this is provided by Core Data)
NSMutableSet* relatedObjects = [self mutableSetValueForKey:key];
for (NSDictionary* relatedObjectDict in relatedObjectDictionaries) {
NSManagedObject* relatedObject =
[NSManagedObject createManagedObjectFromDictionary:relatedObjectDict
inContext:context];
[relatedObjects addObject:relatedObject];
}
}
else if (value != nil) {
[self setValue:[NSManagedObject decodedValueFrom:value forKey:key] forKey:key];
}
}
}
+ (NSManagedObject*) createManagedObjectFromDictionary:(NSDictionary*)dict
inContext:(NSManagedObjectContext*)context
{
NSString* class = [dict objectForKey:@"class"];
NSManagedObject* newObject =
(NSManagedObject*)[NSEntityDescription insertNewObjectForEntityForName:class
inManagedObjectContext:context];
[newObject populateFromDictionary:dict];
return newObject;
}
@end
Owner

pkclsoft commented Feb 15, 2013

This is a categorised version of the code that also allows exports to multiple dictionaries. It might have some memory issues with HUGE databases.

Thank you for the category, that's much cleaner.

I've forked it to fix two issues I found when trying to create managed objects from the dictionary: my entity names didn't match my class names, so I wasn't able to restore serialized classes ("NSInternalInconsistencyException: +entityForName: could not locate an entity...")

Also the DATE_ATTR_PREFIX wasn't removed from the key before setting the value, resulting in exceptions ("NSUnknownKeyException: the entity is not key value coding-compliant for the key 'dAtEaTtr:foo'")

K-Angama commented Sep 1, 2015

For resolve the problem of @nuthatch at the line 132 changed :
[self setValue:[NSManagedObject decodedValueFrom:value forKey:key] forKey:key];
to :
NSString *newKey = [key stringByReplacingOccurrencesOfString:DATE_ATTR_PREFIX withString:@""];
[self setValue:[NSManagedObject decodedValueFrom:value forKey:key] forKey:newKey];

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment