-
-
Save pkclsoft/4958148 to your computer and use it in GitHub Desktop.
| @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 |
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'")
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];
This is a categorised version of the code that also allows exports to multiple dictionaries. It might have some memory issues with HUGE databases.