将JSON数据映射到Object(包括CoreData)

来源:互联网 发布:淘宝买的鲜花能退款吗 编辑:程序博客网 时间:2024/06/06 05:35

PropertyMapping

在进行数据的映射之前,我们需要定义Object(本文中所有Object都表示一个复杂的Object对象)中每一个property的映射关系PropertyMapping,其中ObjectMapping是标示当前property是否是一个Object,如果是nil,则表示当前property就是简单的映射。如果不是nil,则需要将property进行展开,把当前的property当成另外一个Object进行映射。

#import <Foundation/Foundation.h>@class ObjectMapping;@interface PropertyMapping : NSObject@property (nonatomic, weak) ObjectMapping *objectMapping;@property (nonatomic, copy) NSString *sourceKeyPath;@property (nonatomic, copy) NSString *destinationKeyPath;+ (PropertyMapping *)propertyMappingFromKeyPath:(NSString *)sourceKeyPath toKeyPath:(NSString *)destinationKeyPath withMapping:(ObjectMapping *)mapping;@end
#import "PropertyMapping.h"@implementation PropertyMapping+ (PropertyMapping *)propertyMappingFromKeyPath:(NSString *)sourceKeyPath toKeyPath:(NSString *)destinationKeyPath withMapping:(ObjectMapping *)mapping{    PropertyMapping *propertyMapping = [self new];    propertyMapping.sourceKeyPath = sourceKeyPath;    propertyMapping.destinationKeyPath = destinationKeyPath;    propertyMapping.objectMapping = mapping;    return propertyMapping;}@end

ObjectMapping

ObjectMapping管理整个Object的PropertyMapping,包括objectClass,propertyMappings。并且提供Object反映射的inverseMapping。
#import <Foundation/Foundation.h>@class PropertyMapping;@interface ObjectMapping : NSObject- (ObjectMapping *) initWithClass:(Class) objectClass;- (ObjectMapping *) inverseMapping;- (void) addPropertyMapping:(PropertyMapping *) porpertyMapping;//add simple propertys - (void) addPropertyMappingsFromDictionary:(NSDictionary *) dic;@end
#import "ObjectMapping.h"@interface ObjectMapping ()@property (nonatomic, readwrite) Class objectClass;@property (nonatomic, readwrite) NSMutableArray *propertyMappings;@property (nonatomic, readwrite) NSMutableArray *mappedKeyPaths;@end@implementation ObjectMapping+ (instancetype)mappingForClass:(Class)objectClass{    return [[self alloc] initWithClass:objectClass];}- (id)initWithClass:(Class)objectClass{    self = [super init];    if (self)    {        self.objectClass = objectClass;        self.propertyMappings = [NSMutableArray new];        self.mappedKeyPaths = [NSMutableArray new];    }        return self;}- (void)addPropertyMapping:(PropertyMapping *)porpertyMapping{    NSAssert([self.mappedKeyPaths containsObject:porpertyMapping.destinationKeyPath] == NO,              @"Unable to add mapping for keyPath %@, one already exists...", porpertyMapping.destinationKeyPath);    NSAssert(self.propertyMappings, @"mutableRelationshipMappings is nil");    [self.mappedKeyPaths addObject:porpertyMapping.destinationKeyPath];    [self.propertyMappings addObject:porpertyMapping];}- (void)addPropertyMappingsFromDictionary:(NSDictionary *)dic{    for (NSString *attributeKeyPath in dic)    {        [self addPropertyMapping:[PropertyMapping propertyMappingFromKeyPath:attributeKeyPath toKeyPath:[dic objectForKey:attributeKeyPath] withMapping:nil]];    }}- (ObjectMapping *)inverseMapping{    ObjectMapping *inverseMapping = nil;        inverseMapping = [ObjectMapping mappingForClass:[NSMutableDictionary class]];        for (PropertyMapping *propertyMapping in self.propertyMappings)    {        ObjectMapping *inverseRelationshipMapping = nil;        if(propertyMapping.objectMapping && [propertyMapping.objectMapping isKindOfClass:[UPObjectMapping class]])//relationship mapping        {            inverseRelationshipMapping = [propertyMapping.objectMapping inverseMapping];        }        [inverseMapping addPropertyMapping:[PropertyMapping propertyMappingFromKeyPath:propertyMapping.destinationKeyPath toKeyPath:propertyMapping.sourceKeyPath withMapping:inverseRelationshipMapping]];    }        return inverseMapping;}@end

EntityMapping

EntityMapping从ObjectMapping继承,实现从JSONData到CoreData的映射。

#import <Foundation/Foundation.h>#import "ObjectMapping.h"@class ObjectMapping;@interface EntityMapping : ObjectMapping@property (nonatomic, strong) NSEntityDescription *entity;- (id)initWithEntity:(NSEntityDescription *)entity;+ (instancetype)mappingForEntityForName:(NSString *)entityName inManagedObjectStore:(NSPersistentStore *)store;@end
#import "EntityMapping.h"@implementation EntityMapping- (id)initWithEntity:(NSEntityDescription *)entity{    NSParameterAssert(entity);    Class objectClass = NSClassFromString([entity managedObjectClassName]);    NSParameterAssert(objectClass);    self = [self initWithClass:objectClass];    if (self) {        self.entity = entity;    }        return self;}+ (instancetype)mappingForEntityForName:(NSString *)entityName inManagedObjectStore:(NSPersistentStore *)store{    NSParameterAssert(entityName);    NSEntityDescription *entity = [[store.persistentStoreCoordinator.managedObjectModel entitiesByName] objectForKey:entityName];    NSAssert(entity, @"Unable to find an Entity with the name '%@' in the managed object model", entityName);    return [[self alloc] initWithEntity:entity];}@end

MappingOperation
MappingOperation是整个映射的实现,包括从JSONData到Object和从Object到JSONData的映射。

#import <Foundation/Foundation.h>#import "ObjectMapping.h"@interface MappingOperation : NSObject+ (id) mapTargetObjectFromJSONData:(id)jsonData withMapping:(ObjectMapping *)mapping;+ (id) mapJSONDataFromTargetObject:(id)object withMapping:(ObjectMapping *)mapping;@end
#import "MappingOperation.h"@implementation MappingOperation+ (id) mapTargetObjectFromJSONData:(id) jsonData withMapping:(ObjectMapping *) mapping{    NSAssert(jsonData != nil, @"Cannot perform a mapping operation without a sourceObject object");    NSAssert(mapping != nil, @"Cannot perform a mapping operation without a mapping");        NSError *error = nil;        id representation = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];        if (error)    {        NSLog(@"%@", [error description]);        return nil;    }        id mappingResult = [self mapTargetForRepresentation:representation withMapping:mapping];    return mappingResult;}+ (id) mapJSONDataFromTargetObject:(id) object withMapping:(ObjectMapping *) mapping{    NSAssert(object != nil, @"Cannot perform a mapping operation without a sourceObject object");    NSAssert(mapping != nil, @"Cannot perform a mapping operation without a mapping");        id inverseResult = [self mapJSONDatafromRepresentation:object withMapping:mapping];    NSError *error = nil;    id jsonData = [NSJSONSerialization dataWithJSONObject:inverseResult options:NSJSONWritingPrettyPrinted error:&error];    if (error)    {        NSLog(@"%@", [error description]);        return nil;    }    return jsonData;}#pragma private method+ (id) mapTargetForRepresentation:(id) representation withMapping:(ObjectMapping *) mapping{    if(!representation)    {        NSLog(@"Mapping data of %@ is nil.",mapping.objectClass);        return  nil;    }    //handle representation if it is NSArray or NSSet    if([representation isKindOfClass:[NSArray class]] || [representation isKindOfClass:[NSSet class]])    {                //NSLog(@"%i", [representation count]);        NSMutableSet *set = [[NSMutableSet alloc]init];        for(NSDictionary *dic in representation)        {            [set addObject:[self mapTargetForRepresentation:dic withMapping:mapping]];        }        return set;    }        NSObject *result = nil;    if (![mapping isKindOfClass:[EntityMapping class]])    {        result = [[mapping.objectClass alloc]init];    }else    {        EntityMapping *entityMapping = (EntityMapping *)mapping;                NSEntityDescription *entity = [entityMapping entity];        NSString *keyAttribute = [mapping.objectClass keyAttribute];                if(keyAttribute && ![keyAttribute isEqualToString:@""])        {            NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:NSStringFromClass(mapping.objectClass)];            for(UPPropertyMapping *map in mapping.propertyMappings)            {                if([map.destinationKeyPath isEqualToString:keyAttribute])                {                    request.predicate = [NSPredicate predicateWithFormat:@"%K=%@",keyAttribute,[representation valueForKey:map.sourceKeyPath]];                    break;                }            }            NSError *error = nil;            NSArray *exists = [[NSManagedObjectContext contextForCurrentThread] executeFetchRequest:request error:&error];            if ([exists count] > 0)            {                //                result = [exists objectAtIndex:0];                //NSLog(@"object exists:%@", [result valueForKey:keyAttribute]);            }else            {                result = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:[NSManagedObjectContext contextForCurrentThread]];            }        }else        {            result = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:[NSManagedObjectContext contextForCurrentThread]];        }    }            for(UPPropertyMapping *map in mapping.propertyMappings)    {        id value = [self getValueFromRepresentation:representation forKeyPath:map.sourceKeyPath];        if (map.objectMapping)        {            value = [self mapTargetForRepresentation:value withMapping:map.objectMapping];        }        if ([value isKindOfClass:[NSNull class]])        {            value = nil;        }        //[result setValue:value forKey:map.destinationKeyPath];        [self setObject:result withValue:value forKey:map.destinationKeyPath];        //NSLog(@"%@ = %@",map.destinationKeyPath,value);    }        return result;}+ (void) setObject:(id) object withValue:(id) value forKey:(NSString *) keyPath{    NSRange contain = [keyPath rangeOfString:@"."];        //check if the keyPath contains ".", if contain ,then break the keyPath into prefix and suffix    if (contain.length > 0)    {        NSString *prefix = [keyPath substringToIndex:contain.location];        NSString *suffix = [keyPath substringFromIndex:contain.location + 1];                if([[object allKeys] containsObject:prefix])//check if the object has the property of prefix        {            // use the already exist property            id property = [object valueForKey:prefix];            [self setObject:property withValue:value forKey:suffix];        }else        {            //add a property to the object, just handle simple object, can't handle managedobject.            //so when design object mapping, can't add mapping like this @"class1.property":@"class2.property" when class2 is kind of managedobject            id property = [[NSMutableDictionary alloc]init];            [self setObject:property withValue:value forKey:suffix];            [object setValue:property forKey:prefix];        }    }else    {        //handle original property        [object setValue:value forKey:keyPath];    }}+ (id) getValueFromRepresentation:(id) representation forKeyPath:(NSString *) keyPath{    NSRange contain = [keyPath rangeOfString:@"."];        //check if the keyPath contains ".", if contain ,then break the keyPath into prefix and suffix    if (contain.length > 0)    {        NSString *prefix = [keyPath substringToIndex:contain.location];        NSString *suffix = [keyPath substringFromIndex:contain.location + 1];        return [self getValueFromRepresentation:[representation valueForKey:prefix] forKeyPath:suffix];    }    //return original property value    return [representation valueForKey:keyPath];}+ (id) mapJSONDatafromRepresentation:(id) representation withMapping:(ObjectMapping *) mapping{    if(!representation)    {        NSLog(@"Mapping data of %@ is nil.",mapping.objectClass);        return  nil;    }        //handle representation if it is NSArray or NSSet    if([representation isKindOfClass:[NSArray class]] || [representation isKindOfClass:[NSSet class]])    {                //NSLog(@"%i", [representation count]);        NSMutableArray *array = [[NSMutableArray alloc]init];        for(NSDictionary *dic in representation)        {            [array addObject:[self mapJSONDatafromRepresentation:dic withMapping:mapping]];        }        return array;    }        NSObject *result = [[mapping.objectClass alloc]init];        for(UPPropertyMapping *map in mapping.propertyMappings)    {        id value = nil;        if (map.objectMapping)        {            value = [self mapJSONDatafromRepresentation:[representation valueForKey:map.sourceKeyPath] withMapping:map.objectMapping];        }else        {            value = [representation valueForKey:map.sourceKeyPath];        }        [self setObject:result withValue:value forKey:map.destinationKeyPath];    }        return result;}@end