Instantly share code, notes, and snippets.

Embed
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
@pkclsoft

This comment has been minimized.

Copy link
Owner Author

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.

@nuthatch

This comment has been minimized.

Copy link

nuthatch commented May 19, 2013

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

This comment has been minimized.

Copy link

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