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

PropertyMapping

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

#import 

@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 

@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 
#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 
#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






你可能感兴趣的:(IOS)