Giter Site home page Giter Site logo

yalantis / fasteasymapping Goto Github PK

View Code? Open in Web Editor NEW
552.0 43.0 80.0 1.54 MB

A tool for fast serializing & deserializing of JSON

Home Page: https://yalantis.com/

License: Other

Objective-C 92.70% Ruby 0.44% Shell 0.90% Swift 5.95%
objective-c serialization deserialization coredata json-map json-serialization mapping xcode yalantis

fasteasymapping's Introduction

GitHub release CocoaPods Compatible Carthage compatible Build Status codecov.io

FastEasyMapping

Note

This is a fork of EasyMapping, a flexible and easy framework for JSON mapping.

Overview

It turns out, that almost all popular libraries for JSON mapping are SLOW. The main reason for that is multiple trips to database during the lookup of existing objects. We decided to take an already existing flexible solution (i.e. EasyMapping) and improve its overall performance.

Benchmark done on June/2014. Results may be outdated (EasyMapping performs nearly identical nowadays).

FastEasyMapping

Requirements

Platform Min Deployment Target
iOS 8.0
macOS 10.10
tvOS 9.0
watchOS 2.0

Build using Xcode 8.3.2+

Installation

CocoaPods

CocoaPods is a dependency manager for Cocoa projects. You can install it with the following command:

$ gem install cocoapods

To integrate FastEasyMapping into your Xcode project using CocoaPods, specify it in your Podfile:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '10.0'
use_frameworks!

target '<Your Target Name>' do
    pod 'FastEasyMapping', '~> 1.2'
end

Then, run the following command:

$ pod install

Carthage

Carthage is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.

You can install Carthage with Homebrew using the following command:

$ brew update
$ brew install carthage

To integrate FastEasyMappingRealm into your Xcode project using Carthage, specify it in your Cartfile:

github "Yalantis/FastEasyMapping" ~> 1.2

Run carthage update to build the framework and drag the built FastEasyMapping.framework into your Xcode project.

Usage

Deserialization

Today NSObject and NSManagedObject mapping are supported out of the box. Lets take a look at how a basic mapping looks like: For example, we have JSON:

{
    "name": "Lucas",
    "user_email": "[email protected]",
    "car": {
        "model": "i30",
        "year": "2013"
    },
    "phones": [
        {
            "ddi": "55",
            "ddd": "85",
            "number": "1111-1111"
        },
        {
            "ddi": "55",
            "ddd": "11",
            "number": "2222-222"
        }
    ]
}

and corresponding CoreData-generated classes:

@interface Person : NSManagedObject

@property (nonatomic, retain) NSString *name;
@property (nonatomic, retain) NSString *email;
@property (nonatomic, retain) Car *car;
@property (nonatomic, retain) NSSet *phones;

@end

@interface Car : NSManagedObject

@property (nonatomic, retain) NSString *model;
@property (nonatomic, retain) NSString *year;
@property (nonatomic, retain) Person *person;

@end

@interface Phone : NSManagedObject

@property (nonatomic, retain) NSString *ddi;
@property (nonatomic, retain) NSString *ddd;
@property (nonatomic, retain) NSString *number;
@property (nonatomic, retain) Person *person;

@end

In order to map JSON to Object and vice versa we have to describe the mapping rules:

@implementation Person (Mapping)

+ (FEMMapping *)defaultMapping {
    FEMMapping *mapping = [[FEMMapping alloc] initWithEntityName:@"Person"];
    [mapping addAttributesFromArray:@[@"name"]];
    [mapping addAttributesFromDictionary:@{@"email": @"user_email"}];

    [mapping addRelationshipMapping:[Car defaultMapping] forProperty:@"car" keyPath:@"car"];
    [mapping addToManyRelationshipMapping:[Phone defaultMapping] forProperty:@"phones" keyPath:@"phones"];

    return mapping;
}

@end

@implementation Car (Mapping)

+ (FEMMapping *)defaultMapping {
    FEMMapping *mapping = [[FEMMapping alloc] initWithEntityName:@"Car"];
    [mapping addAttributesFromArray:@[@"model", @"year"]];

    return mapping;
}

@end


@implementation Phone (Mapping)

+ (FEMMapping *)defaultMapping {
    FEMMapping *mapping = [[FEMMapping alloc] initWithEntityName:@"Phone"];
    [mapping addAttributesFromArray:@[@"number", @"ddd", @"ddi"]];

    return mapping;
}

@end

Now we can deserialize JSON to Object easily:

FEMMapping *mapping = [Person defaultMapping];
Person *person = [FEMDeserializer objectFromRepresentation:json mapping:mapping context:managedObjectContext];

Or collection of Objects:

NSArray *persons = [FEMDeserializer collectionFromRepresentation:json mapping:mapping context:managedObjectContext];

Or even update an Object:

[FEMDeserializer fillObject:person fromRepresentation:json mapping:mapping];

Serialization

Also we can serialize an Object to JSON using the mapping defined above:

FEMMapping *mapping = [Person defaultMapping];
Person *person = ...;
NSDictionary *json = [FEMSerializer serializeObject:person usingMapping:mapping];

Or collection to JSON:

FEMMapping *mapping = [Person defaultMapping];
NSArray *persons = ...;
NSArray *json = [FEMSerializer serializeCollection:persons usingMapping:mapping];

Mapping

FEMAttribute

FEMAttribute is a core class of FEM. Briefly it is a description of relationship between the Object's property and the JSON's keyPath. Also it encapsulates knowledge of how the value needs to be mapped from Object to JSON and back via blocks.

typedef __nullable id (^FEMMapBlock)(id value __nonnull);

@interface FEMAttribute : NSObject <FEMProperty>

@property (nonatomic, copy, nonnull) NSString *property;
@property (nonatomic, copy, nullable) NSString *keyPath;

- (nonnull instancetype)initWithProperty:(nonnull NSString *)property keyPath:(nullable NSString *)keyPath map:(nullable FEMMapBlock)map reverseMap:(nullable FEMMapBlock)reverseMap;

- (nullable id)mapValue:(nullable id)value;
- (nullable id)reverseMapValue:(nullable id)value;

@end

Alongside with property and keyPath value you can pass mapping blocks that allow to describe completely custom mappings.

Examples:

Mapping of value with the same keys and type:

FEMAttribute *attribute = [FEMAttribute mappingOfProperty:@"url"];
// or 
FEMAttribute *attribute = [[FEMAttribute alloc] initWithProperty:@"url" keyPath:@"url" map:NULL reverseMap:NULL];

Mapping of value with different keys and the same type:

FEMAttribute *attribute = [FEMAttribute mappingOfProperty:@"urlString" toKeyPath:@"URL"];
// or 
FEMAttribute *attribute = [[FEMAttribute alloc] initWithProperty:@"urlString" keyPath:@"URL" map:NULL reverseMap:NULL];

Mapping of different types:

Quite often value type in JSON needs to be converted to more useful internal representation. For example HEX to UIColor, String to NSURL, Integer to enum and so on. For this purpose you can use map and reverseMap properties. For example lets describe attribute that maps String to NSDate using NSDateFormatter:

NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]];
[formatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
[formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];

FEMAttribute *attribute = [[FEMAttribute alloc] initWithProperty:@"updateDate" keyPath:@"timestamp" map:^id(id value) {
	if ([value isKindOfClass:[NSString class]]) {
		return [formatter dateFromString:value];
	} 
	return nil;
} reverseMap:^id(id value) {
	return [formatter stringFromDate:value];
}];

First of all we've defined NSDateFormatter that fits our requirements. Next step is to define Attribute instance with correct mapping. Briefly map block is invoked during deserialization (JSON to Object) while reverseMap is used for serialization process. Both are quite stratforward with but with few gotchas:

  • map can receive NSNull instance. This is a valid case for null value in JSON.
  • map won't be invoked for missing keys. Therefore, if JSON doesn't contain keyPath specified by your attribute, reverse mapping not called.
  • from map you can return either nil or NSNull for empty values
  • reverseMap invoked only when property contains a non-nil value.
  • from reverseMap you can return either nil or NSNull. Both will produce {"keyPath": null}

Adding attribute to FEMMapping

There are several shortcuts that allow you to add attributes easier to the mapping itself:

Explicitly
FEMMapping *mapping = [[FEMMapping alloc] initWithObjectClass:[Person class]];
FEMAttribute *attribute = [FEMAttribute mappingOfProperty:@"url"];
[mapping addAttribute:attribute];
Implicitly
FEMMapping *mapping = [[FEMMapping alloc] initWithObjectClass:[Person class]];
[mapping addAttributeWithProperty:@"property" keyPath:@"keyPath"];
As a Dictionary
FEMMapping *mapping = [[FEMMapping alloc] initWithObjectClass:[Person class]];
[mapping addAttributesFromDictionary:@{@"property": @"keyPath"}];
As an Array

Useful when the property is equal to the keyPath:

FEMMapping *mapping = [[FEMMapping alloc] initWithObjectClass:[Person class]];
[mapping addAttributesFromArray:@[@"propertyAndKeyPathAreTheSame"]];

FEMRelationship

FEMRelationship is a class that describes relationship between two FEMMapping instances.

@interface FEMRelationship

@property (nonatomic, copy, nonnull) NSString *property;
@property (nonatomic, copy, nullable) NSString *keyPath;

@property (nonatomic, strong, nonnull) FEMMapping *mapping;
@property (nonatomic, getter=isToMany) BOOL toMany;

@property (nonatomic) BOOL weak;
@property (nonatomic, copy, nonnull) FEMAssignmentPolicy assignmentPolicy;

@end

Relationship is also bound to a property and keyPath. Obviously, it has a reference to Object's FEMMapping and a flag that indicates whether it’s a to-many relationship. Moreover, it allows you to specify assignment policy and "weakifying" behaviour of the relationship.

Example:

FEMMapping *childMapping = ...;

FEMRelationship *childRelationship = [[FEMRelationship alloc] initWithProperty:@"parentProperty" keyPath:@"jsonKeyPath" mapping:childMapping];
childRelationship.toMany = YES;

Assignment policy

Assignment policy describes how deserialized relationship value should be assigned to a property. FEM supports 5 policies out of the box:

  • FEMAssignmentPolicyAssign - replace Old property's value by New. Designed for to-one and to-many relationship. Default policy.
  • FEMAssignmentPolicyObjectMerge - assigns New relationship value unless it is nil. Designed for to-one relationship.
  • FEMAssignmentPolicyCollectionMerge - merges a New and Old values of relationship. Supported collections are: NSSet, NSArray, NSOrderedSet and their successors. Designed for to-many relationship.
  • FEMAssignmentPolicyObjectReplace - replaces Old value with New by deleting Old. Designed for to-one relationship.
  • FEMAssignmentPolicyCollectionReplace - deletes objects not presented in union of New and Old values sets. Union set is used as a New value. Supported collections are: NSSet, NSArray, NSOrderedSet and their successors. Designed for to-many relationship.

Adding relationship to FEMMapping

Explicitly
FEMMapping *mapping = [[FEMMapping alloc] initWithObjectClass:[Person class]];
FEMMapping *carMapping = [[FEMMapping alloc] initWithObjectClass:[Car class]];

FEMRelationship *carRelationship = [[FEMRelationship alloc] initWithProperty:@"car" keyPath:@"car" mapping:carMapping];
[mapping addRelationship:carRelationship];
Implicitly
FEMMapping *mapping = [[FEMMapping alloc] initWithObjectClass:[Person class]];
FEMMapping *phoneMapping = [[FEMMapping alloc] initWithObjectClass:[Phone class]];

[mapping addToManyRelationshipMapping:phoneMapping property:@"phones" keyPath:@"phones"];

FEMMapping

Generally FEMMapping is a class that describes mapping for NSObject or NSManagedObject by encapsulating a set of attributes and relationships. In addition, it defines the possibilities for objects uniquing (supported by CoreData only).

The only difference between NSObject and NSManagedObject is in init methods:

NSObject
FEMMapping *objectMapping = [[FEMMapping alloc] initWithObjectClass:[CustomNSObjectSuccessor class]];
NSManagedObject
FEMMapping *managedObjectMapping = [[FEMMapping alloc] initWithEntityName:@"EntityName"];

Root Path

Sometimes a desired JSON is nested by a keyPath. In this case you can use rootPath property. Let’s modify Person JSON by nesting Person representation:

{
	result: {
		"name": "Lucas",
    	"user_email": "[email protected]",
    	"car": {
        	"model": "i30",
        	"year": "2013"
    	}
	}
}

Mapping will look like this:

@implementation Person (Mapping)

+ (FEMMapping *)defaultMapping {
    FEMMapping *mapping = [[FEMMapping alloc] initWithEntityName:@"Person"];
    mapping.rootPath = @"result";

    [mapping addAttributesFromArray:@[@"name"]];
    [mapping addAttributesFromDictionary:@{@"email": @"user_email"}];
    [mapping addRelationshipMapping:[Car defaultMapping] forProperty:@"car" keyPath:@"car"];
  
  	return mapping;
}

@end

IMPORTANT: FEMMapping.rootPath is ignore during relationship mapping. Use FEMRelationship.keyPath instead!

Uniquing

It is a common case when you're deserializing JSON into CoreData and don't want to duplicate data in your database. This can be easily achieved by utilizing FEMMapping.primaryKey. It informs FEMDeserializer to track primary keys and avoid data copying. For example lets make Person's email a primary key attribute:

@implementation Person (Mapping)

+ (FEMMapping *)defaultMapping {
    FEMMapping *mapping = [[FEMMapping alloc] initWithEntityName:@"Person"];
    mapping.primaryKey = @"email";
    [mapping addAttributesFromArray:@[@"name"]];
    [mapping addAttributesFromDictionary:@{@"email": @"user_email"}];

    [mapping addRelationshipMapping:[Car defaultMapping] forProperty:@"car" keyPath:@"car"];
    
    return mapping;
}

@end

We recommend to index your primary key in datamodel to speedup keys lookup. Supported values for primary keys are Strings and Integers.

Starting from second import FEMDeserializer will update existing Person.

Relationship bindings by PK

Sometimes object representation contains a relationship described by a PK of the target entity:

{
	"result": {
		"id": 314
		"title": "https://github.com"
		"category": 4
	}
}

As you can see, from JSON we have two objects: Website and Category. If Website can be imported easily, there is an external reference to a Category represented by its primary key id. Can we bind the Website to the corresponding category? Yep! We just need to treat Website's representation as a Category:

First of all let’s declare our classes:

@interface Website: NSManagedObject

@property (nonatomic, strong) NSNumber *identifier;
@property (nonatomic, strong) NSString *title;
@property (nonatomic, strong) Category *category;

@end

@interface Category: NSManagedObject

@property (nonatomic, strong) NSNumber *identifier;
@property (nonatomic, strong) NSString *title;
@property (nonatomic, strong) NSSet *websites

@end

Now it is time to define mapping for Website:

@implementation Website (Mapping)

+ (FEMMapping *)defaultMapping {
	FEMMapping *mapping = [[FEMMapping alloc] initWithEntityName:@"Website"];
	mapping.primaryKey = @"identifier";
	[mapping addAttributesFromDictionary:@{@"identifier": @"id", @"title": @"title"}];

	FEMMapping *categoryMapping = [[FEMMapping alloc] initWithEntityName:@"Category"];
	categoryMapping.primaryKey = @"identifier";
	[categoryMapping addAttributesFromDictionary:@{@"identifier": @"category"}];

	[mapping addRelationshipMapping:categoryMapping property:@"category" keyPath:nil];

	return mapping;
}

@end

By specifying nil as a keyPath for the category Website's representation is treated as a Category at the same time. In this way it is easy to bind objects that are passed by PKs (which is quite common for network).

Weak relationship

In the example above there is an issue: what if our database doesn't contain Category with PK = 4? By default FEMDeserializer creates new objects during deserialization lazily. In our case this leads to insertion of Category instance without any data except identifier. In order to prevent such inconsistencies we can set FEMRelationship.weak to YES:

@implementation Website (Mapping)

+ (FEMMapping *)defaultMapping {
    FEMMapping *mapping = [[FEMMapping alloc] initWithEntityName:@"Website"];
    mapping.primaryKey = @"identifier";
    [mapping addAttributesFromDictionary:@{@"identifier": @"id", @"title": @"title"}];

    FEMMapping *categoryMapping = [[FEMMapping alloc] initWithEntityName:@"Category"];
    categoryMapping.primaryKey = @"identifier";
    [categoryMapping addAttributeWithProperty:@"identifier" keyPath:nil];

    FEMRelationship *categoryRelationship = [[FEMRelationship alloc] initWithProperty:@"category" keyPath:@"category" mapping:categoryMapping];
    categoryRelationship.weak = YES;

    [mapping addRelationship:categoryRelationship];

    return mapping;
}

@end

As a result it'll bind the Website with the corresponding Category only if the latter exists.

Delegation

You can customize deserialization process by implementing FEMDeserializerDelegate protocol:

@protocol FEMDeserializerDelegate <NSObject>

@optional
- (void)deserializer:(nonnull FEMDeserializer *)deserializer willMapObjectFromRepresentation:(nonnull id)representation mapping:(nonnull FEMMapping *)mapping;
- (void)deserializer:(nonnull FEMDeserializer *)deserializer didMapObject:(nonnull id)object fromRepresentation:(nonnull id)representation mapping:(nonnull FEMMapping *)mapping;

- (void)deserializer:(nonnull FEMDeserializer *)deserializer willMapCollectionFromRepresentation:(nonnull NSArray *)representation mapping:(nonnull FEMMapping *)mapping;
- (void)deserializer:(nonnull FEMDeserializer *)deserializer didMapCollection:(nonnull NSArray *)collection fromRepresentation:(nonnull NSArray *)representation mapping:(nonnull FEMMapping *)mapping;

@end

However, if you're using Delegate you also have to instantiate FEMDeserializer manually:

NSObject
FEMDeserializer *deserializer = [[FEMDeserializer alloc] init];
deserializer.delegate = self;
NSManagedObject
FEMDeserializer *deserializer = [[FEMDeserializer alloc] initWithContext:managedObjectContext];
deserializer.delegate = self;

Note, that delegate methods will be called on every object and collection during deserialization. Lets use Person example:

{
    "name": "Lucas",
    "user_email": "[email protected]",
    "phones": [
        {
            "ddi": "55",
            "ddd": "85",
            "number": "1111-1111"
        }
    ]
}

Mapping:

@implementation Person (Mapping)

+ (FEMMapping *)defaultMapping {
    FEMMapping *mapping = [[FEMMapping alloc] initWithEntityName:@"Person"];
    [mapping addAttributesFromArray:@[@"name"]];
    [mapping addAttributesFromDictionary:@{@"email": @"user_email"}];
    [mapping addToManyRelationshipMapping:[Person defaultMapping] forProperty:@"phones" keyPath:@"phones"];

    return mapping;
}

@end

@implementation Phone (Mapping)

+ (FEMMapping *)defaultMapping {
    FEMMapping *mapping = [[FEMMapping alloc] initWithEntityName:@"Phone"];
    [mapping addAttributesFromArray:@[@"number", @"ddd", @"ddi"]];

    return mapping;
}

@end

During deserialization of persons collection order will be the following:

  1. willMapCollectionFromRepresentation:Persons Array mapping:Person mapping
  2. willMapObjectFromRepresentation:Person Dictionary mapping:Person mapping
  3. willMapCollectionFromRepresentation:Phones Array mapping:Phone mapping
  4. willMapObjectFromRepresentation:Phone Dictionary mapping:Phone mapping
  5. didMapObject:Phone instance fromRepresentation:Phone Dictionary mapping:Phone mapping
  6. didMapObject:Person instance fromRepresentation:Person Dictionary mapping:Person mapping
  7. didMapCollection:Persons instances Array fromRepresentation:Persons Array mapping:Person mapping

Thanks

Extra

Read out blogpost about FastEasyMapping.

fasteasymapping's People

Contributors

andreyoshev avatar dimazen avatar dmytrozavgorodniy avatar jcavar avatar k06a avatar olegmelnik avatar readmecritic avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fasteasymapping's Issues

[question] addToManyRelationshipMapping for same entity

Does the library cover the following case ?

screen shot 2015-07-01 at 11 55 18 am

+ (FEMManagedObjectMapping*)userMapping {

    FEMMappingConfigurationBlock fieldsToMap = ^void(FEMManagedObjectMapping *mapping) {
        [mapping setPrimaryKey:@"identifier"];

/*
Populate fields data
*/

        // relationship to same entity causes an infinite loop
        [mapping addToManyRelationshipMapping: [self userMapping]
                                  forProperty: @"relatives"
                                      keyPath: @"relatives"];
    };


    // the "configuration" block is called immediately
    return [FEMManagedObjectMapping mappingForEntityName: @"HLJUser"
                                           configuration: fieldsToMap]; 
}

Issue in enforcing MergePolicy of NSManagedObjectContext

Hi,

My code looks like bellow

 _privateObjectContext = [[NSManagedObjectContext alloc]   initWithConcurrencyType:NSPrivateQueueConcurrencyType];
 [_privateObjectContext setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
 _privateObjectContext.parentContext = self.mainObjectContext;

 FEMMapping *mapping = [Users defaultMapping];
 NSArray *array= [FEMDeserializer collectionFromRepresentation:responseArray
                                                          mapping:mapping context:_privateObjectContext];

and I have set constraint on userId like this - https://puu.sh/qE7Uw/314fa1dc42.png
The above code does not merge the duplicate object, and resulted duplicate rows like

          userId           |               userName
   --------------------------------------------------------------
           1              |                John Doe
  ---------------------------------------------------------------
           1              |                John Doe
  ---------------------------------------------------------------

If change the above code to

 _privateObjectContext = [[NSManagedObjectContext alloc]   initWithConcurrencyType:NSPrivateQueueConcurrencyType];
 [_privateObjectContext setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
 _privateObjectContext.parentContext = self.mainObjectContext;

 FEMMapping *mapping = [Users defaultMapping];
 NSArray *array= [FEMDeserializer collectionFromRepresentation:responseArray
                                                          mapping:mapping context:_privateObjectContext];
 [_privateObjectContext save:nil];

the the Duplicate objects merge properly and result in a single row, like

          userId           |               userName
   --------------------------------------------------------------
           1              |                John Doe
  ---------------------------------------------------------------

Which is kind of a wired, because the same context is passed and save is call on the same context in the commitTransaction method.

I know there is another way using PK FEM resolves duplicates, but I need to resolve this in merge time using a MergePolicy.

is the example of Weak relationship in README.md correct?

Hi, In the README the example of weak is shown like below

    FEMMapping *categoryMapping = [[FEMMapping alloc] initWithEntityName:@"Category"];
    categoryMapping.primaryKey = @"identifier";
    categoryMapping.weak = YES;

FEMMapping doesn't have a visible method/property named weak, though FEMRelationship have a BOOL property name weak

Invalid mapping of NULL datetime

If input datetime is NULL - it is ignored, and current value doesn't get replaced.
expected behaviour: replace current value with nil

Mapping complicated arrays return NSUnknownKeyException

Now I try to parse JSON from Wordpress API ( https://developer.wordpress.com/docs/api/1/get/sites/%24site/posts/ ), i can mapping almost fields of "post" object but I don't know how to do with "tags", "categories" and "attachments" fields. Here's my code:

  • (FEMManagedObjectMapping *)postMapping {
    return [FEMManagedObjectMapping mappingForEntityName:@"PostWP" configuration:^(FEMManagedObjectMapping *mapping) {
    [mapping setPrimaryKey:@"post_id"]; // object uniquing

    [mapping addAttributeMappingDictionary:@{@"post_id": @"ID"}];
    [mapping addAttributeMappingDictionary:@{@"site_id": @"site_ID"}];
    [mapping addAttributeMappingFromArray:@[@"date", @"modified", @"title", @"content", @"excerpt", @"slug", @"guid", @"status", @"sticky", @"password", @"parent", @"type", @"likes_enabled", @"sharing_enabled"]];
    [mapping addAttributeMappingDictionary:@{@"url": @"URL"}];
    [mapping addAttributeMappingDictionary:@{@"short_url": @"short_URL"}];
    
    [mapping addRelationshipMapping:[self authorMapping] forProperty:@"author" keyPath:@"author"];
    [mapping addToManyRelationshipMapping:[self tagMapping] forProperty:@"tags" keyPath:@"tags"];
    [mapping addToManyRelationshipMapping:[self categoryMapping] forProperty:@"categories" keyPath:@"categories"];
    

    }];
    }

  • (FEMManagedObjectMapping *)authorMapping {
    return [FEMManagedObjectMapping mappingForEntityName:@"AuthorWP" configuration:^(FEMManagedObjectMapping *mapping) {
    [mapping setPrimaryKey:@"author_id"];
    [mapping addAttributeMappingDictionary:@{@"author_id":@"ID"}];
    [mapping addAttributeMappingDictionary:@{@"url":@"URL"}];
    [mapping addAttributeMappingDictionary:@{@"avatar_url":@"avatar_URL"}];
    [mapping addAttributeMappingDictionary:@{@"profile_url":@"profile_URL"}];
    [mapping addAttributeMappingDictionary:@{@"site_id":@"site_ID"}];
    [mapping addAttributeMappingFromArray:@[@"login", @"email", @"name", @"nice_name"]];
    }];
    }

  • (FEMManagedObjectMapping *)tagMapping {
    return [FEMManagedObjectMapping mappingForEntityName:@"TagWP" configuration:^(FEMManagedObjectMapping *mapping) {
    [mapping addAttributeMappingDictionary:@{@"tag_id":@"ID"}];
    [mapping addAttributeMappingDictionary:@{@"tag_description":@"description"}];
    [mapping addAttributeMappingFromArray:@[@"name", @"slug", @"post_count"]];
    }];
    }

  • (FEMManagedObjectMapping *)categoryMapping {
    return [FEMManagedObjectMapping mappingForEntityName:@"CategoryWP" configuration:^(FEMManagedObjectMapping *mapping) {
    [mapping addAttributeMappingDictionary:@{@"category_id":@"ID"}];
    [mapping addAttributeMappingDictionary:@{@"category_description":@"description"}];
    [mapping addAttributeMappingFromArray:@[@"name", @"slug", @"post_count", @"parent"]];
    }];
    }

Here's the error message:

*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<__NSCFString 0x7fc3a2f3f0c0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key slug.'

Best way to track changes

I am using CoreData and trying to track when my entity field logo_url is changed so I can download new logo right after entities updating finished. I there any less code solution other then remembering old values before updating and comparing them with new values?

Support for same class nested objects ?

I am trying to use FastEasyMapping to support nested objects of the same type.

For example, a class Person might have an array of Person (children...) :

@interface Person:NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSArray *children;
@end

+ (FEMObjectMapping *)personMapping {
    return [FEMObjectMapping mappingForClass:Person.class configuration:^(FEMObjectMapping *mapping) {
        [mapping addAttributesFromArray:@[@"name"]];
        [mapping addToManyRelationshipMapping:self.personMapping forProperty:@"children" keyPath:@"children"];
    }];
}

This obviously ends up in an infinite loop...

Is there a way to use FastEasyMapping with this setup ?

Crash on array [NSKeyedArchiver archivedDataWithRootObject:array]

a am trying to map

[mapping addAttributeMappingDictionary:@{@"ingredienti" : [NSKeyedArchiver archivedDataWithRootObject:array]

where "ingredienti" is a Binary Data property od core data but crash with error:

-[_NSInlineData characterAtIndex:]: unrecognized selector sent to instance

is my fault or is a bug?

[feature] Mapping dictionary keys to entity properties

Having a server response (chunk)

"thumbnails":
{
     "120x79” : ”<url>”, 
    “800x600” : "<another URL>" 
}

I'd like to get two entities of "thumbnail" type having both dictionary key and its value stored in its properties.

@interface Thumbnail : NSManagedObject

@property (nonatomic, retain) NSString * imageDimensions; //  @"120x79”
@property (nonatomic, retain) NSString * url; // "<url>”

@end

Remove relationships mapping ambiguity

Now user is able to specify - [FEMMapping rootPath] as well, as - [FEMRelationshipMapping keyPath] which leads to ambiguity, since in pair "relationship-mapping" mapping.keyPath is ignored.

Resolution: remove keyPath for FEMRelationshipMapping

Is there any JSON Mapping faster than FastEasyMapping?

I have 100k rows of data with 100 fields. I have integrate EasyMapping and it takes about 8 seconds to map it to object class. Since FastEasyMapping created for performance issue, do you know any other ways to even improve the mapping performance?

addRelationshipMapping issue

I have user entity in card entity. Card have relation to user. 1-1
{
"user": {
"phone": "11111111111",
"active": true
}

....
}

In Card mapping:
[mapping addRelationshipMapping:[User mapObject]
forProperty:@"creator"
keyPath:@"user"];

In Card manager:
FEMManagedObjectMapping *mapping = [Card cardMapping];
[FEMManagedObjectDeserializer fillObject:card fromExternalRepresentation:cardDict usingMapping:mapping];

Show next issue:
-[User count]: unrecognized selector sent to instance 0x7fa863d49680
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[User count]: unrecognized selector sent to instance 0x7fa863d49680'

*** First throw call stack:
(
0 CoreFoundation 0x000000010998ff35 exceptionPreprocess + 165
1 libobjc.A.dylib 0x00000001092cdbb7 objc_exception_throw + 45
2 CoreFoundation 0x000000010999704d -[NSObject(NSObject) doesNotRecognizeSelector:] + 205
3 CoreFoundation 0x00000001098ef27c __forwarding
+ 988
4 CoreFoundation 0x00000001098eee18 _CF_forwarding_prep_0 + 120
5 CoreFoundation 0x00000001098e9bee +[NSSet setWithArray:] + 46
6 Welpy 0x00000001047d9d73 +[FEMManagedObjectDeserializer _fillObject:fromRepresentation:usingMapping:] + 2595
7 --- 0x00000001047da151 +[FEMManagedObjectDeserializer fillObject:fromExternalRepresentation:usingMapping:] + 321
8 --- 0x00000001046e4b50 __37-[CardsManager saveCards:completion:]_block_invoke + 960
9 --- 0x0000000104a3771a __51+[MagicalRecord(Actions) saveWithBlock:completion:]_block_invoke + 58
10 CoreData 0x0000000105c96f06 developerSubmittedBlockToNSManagedObjectContextPerform + 198
11 libdispatch.dylib 0x000000010a0ea7f4 _dispatch_client_callout + 8
12 libdispatch.dylib 0x000000010a0d2b22 _dispatch_queue_drain + 1417
13 libdispatch.dylib 0x000000010a0d2432 _dispatch_queue_invoke + 235
14 libdispatch.dylib 0x000000010a0d4fc1 _dispatch_root_queue_drain + 685
15 libdispatch.dylib 0x000000010a0d6612 _dispatch_worker_thread2 + 52
16 libsystem_pthread.dylib 0x000000010a482ef8 _pthread_wqthread + 314
17 libsystem_pthread.dylib 0x000000010a485fb9 start_wqthread + 13
)
libc++abi.dylib: terminating with uncaught exception of type NSException

Copyright infringement

As stated in MIT license, original author of the code should be mentioned in any copy or substantial portion of the software.

If you don't wan't to be marked as thieves, you probably should include mentions of original author of the library in the code of your library.

And just out of interest, why not sending PR to EasyMapping?

Primary Key from multiple attribute

Hello,

Is there any way to have multiple attributes as a primary key? I have an object that has 2 string attributes and combination of them determines the object uniqueness.

Thanks.

Best way to map subrequest without manual add/remove

I am using request to get places:

http://.../api/json/GetPlaces

Then I need to make request:

http://.../api/json/GetPlaceMenu?place_id=%@

I am doing this way:

NSArray *places = [FEMManagedObjectDeserializer deserializeCollectionExternalRepresentation:responseJSON usingMapping:[Place mapping] context:[NSManagedObjectContext MR_defaultContext]];

For each place I am doing request and mapping like this this:

NSArray *menus_arr = [FEMManagedObjectDeserializer deserializeCollectionExternalRepresentation:responseJSON usingMapping:[Menu mapping] context:[NSManagedObjectContext MR_defaultContext]];
NSSet *menus = [NSSet setWithArray:menus_arr];

And then I am trying to determine menus to add and to remove from place objects:

NSMutableSet *menusToAdd = [NSMutableSet set];
for (Menu *menu in menus) {
    if ([place.menus objectsPassingTest:^BOOL(Menu *obj, BOOL *stop) { return [obj.menu_id isEqual:menu.menu_id]; }].count == 0)
        [menusToAdd addObject:menu];
}
NSMutableSet *menusToRemove = [NSMutableSet set];
for (Menu *menu in place.menus) {
    if ([menus objectsPassingTest:^BOOL(Menu *obj, BOOL *stop) { return [obj.menu_id isEqual:menu.menu_id]; }].count == 0)
        [menusToRemove addObject:menu];
}

[place addMenus:menusToAdd];
[place removeMenus:menusToRemove];

Really I don't wanna use this code :) I wanna Fast and Easy Mapping. Please tell me how to avoid this code with FAM.

conditional mapping

How to pass additional parameter to defaultMapping method, so I can map conditionally like bellow

    if(someExternalFlag) {
         [mapping addAttributesFromDictionary:@{@"primaryID": @"user_email"}];
    } else {
         [mapping addAttributesFromDictionary:@{@"primaryID": @"user_phone"}];
    }

Invalid URL mapping

If input value is NULL - it is ignored, and current value doesn't get replaced.
expected behaviour: replace current value with nil

nil in relationships

Maybe it bug comes from #6 , but what I got:

  1. I'm using mapping for NsManagedObject - let's call him myProfileEntity
  2. it has attributes, and 3 relationships, 1-1, and two 1-ToMany
  3. on first event - to myProfile I try to map data from json, where NO RELATIONSHIP data.
  4. got crash on assert in: FEMCache.m : line 110
    Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Expected container classes: NSArray, NSDictionary. Got:(null)

This issue comes from last pod update.
Can you suggest something ?
Thanks

Get current context in mapping

Is it possible to get a current context in mapping in 1.0 like it was before with FEMCacheGetCurrent()?
Or maybe there's some other workaround exists for such cases?

[mapping addAttribute:[FEMAttributeMapping mappingOfProperty:@"client" toKeyPath:@"client.id" map:^id(id value) {
    return [Client MR_findFirstByAttribute:@"external_id" withValue:value inContext:[FEMCacheGetCurrent() context]];
}]];

Connecting Non-Nested Relationships Using Foreign Keys

Hello.

FastEasyMapping support non-nested relationships using foreign keys like RestKit?

Connecting Non-Nested Relationships Using Foreign Keys

JSON attributes are sometimes foreign key identifiers to existing Core Data entities, such as entities obtained by a separate RKObjectRequestOperation. RestKit provides a method to map to an entity
using a foreign key identifier. Consider the following JSON:

{ "cities": [
      { "cityID": 1, "cityName": "Chicago" },
      { "cityID": 2, "cityName": "New York" }],
  "users": [
      { "userID":101, "userName": "Jeff", "cityID": 1 },
      { "userID":102, "userName": "Mary", "cityID": 2 },
      { "userID":103, "userName": "Sam",  "cityID": 1 }]
}

and the corresponding models where User has a Core Data relationship to City:

@interface City: NSManagedObject
    @property (nonatomic, retain) NSNumber* cityID;
    @property (nonatomic, retain) NSString* cityName;
@end

@interface User: NSManagedObject
    @property (nonatomic, retain) NSNumber* userID;
    @property (nonatomic, retain) NSString* userName;
    @property (nonatomic, retain) NSNumber* cityID;
    @property (nonatomic, retain) City* city;
@end

To connect the User entity to the City entity referenced by cityID property, you do the following after creating the RKEntityMapping for the User:

[userMapping addConnectionForRelationship:@"city" connectedBy:@{@"cityID": @"cityID"}];

The relationship value @"city" specifies the relationship object or name of the relationship object that is to be connected. The connectedBy: parameter is an NSDictionary mapping the User attribute to the City attribute. Since the attribute names are identical, the connectedBy: parameter can be also specified by an NSString, i.e. @"cityID".

AFNetworking + FastEasyMapping не маппит объекты из JSON

Пишу на русском заведомо зная, что компания Украинская. Надеюсь на очень быструю помощь)
C сервера приходит следующий json:

{
    "auto_load_more_enabled" = 1;
    "is_direct_v2_enabled" = 1;
    items =     (
                {
            caption =             {
                "bit_flags" = 0;
                "content_type" = comment;
                "created_at" = 1443217230;
                "created_at_utc" = 1443246030;
                "media_id" = 1082094397765659443;
                pk = 1082107015322609678;
                status = Active;
                text = "text";
                type = 1;
                user =                 {
                    "full_name" = "name";
                    "is_private" = 0;
                    pk = 123213213;
                    "profile_pic_url" = "<url>";
                    username = julianafed;
                };
                "user_id" = 197974297;
            };
            "caption_is_edited" = 1;
            code = 8EXx8gQysz;
            "comment_count" = 1;
            comments =             (
            );
            "device_timestamp" = 1443215597;
            "has_liked" = 0;
            "has_more_comments" = 0;
            id = "1082094397765659443_197974297";
            "image_versions2" =             {
                candidates =                 (
                                        {
                        height = 1080;
                        url = "<url>";
                        width = 1080;
                    },
                                        {
                        height = 750;
                        url = "<url>";
                        width = 750;
                    }
                );
            };
            "like_count" = 19;
            "media_type" = 1;
            "original_height" = 1080;
            "original_width" = 1080;
            "photo_of_you" = 0;
            pk = 1082094397765659443;
            "taken_at" = 1443215726;
            user =             {
                "full_name" = "name";
                "has_anonymous_profile_picture" = 0;
                "is_private" = 0;
                "is_unpublished" = 0;
                pk = 197974297;
                "profile_pic_url" = "<url>";
                username = julianafed;
            };
        },
    ....
  )
}

Сам запрос и передача NSDictionary на маппинг:

- (void)getTimelineFeedWithSuccess:(void(^)(AFHTTPRequestOperation *operation, NSArray *mappingResult))success
                           failure:(void(^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
    [_requestOperationManager GET:@"/api/v1/feed/timeline/"
                       parameters:nil
                          success:^(AFHTTPRequestOperation *operation, NSDictionary *responseObject) {
                              FEMMapping *mapping = [Media defaultMapping];
                              NSArray *mappingResult = [FEMDeserializer collectionFromRepresentation:[responseObject objectForKey:@"items"]
                                                                                             mapping:mapping
                                                                                             context:SharedAppDelegate.managedObjectContext];

                              success(operation, mappingResult);
                          } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                              failure(operation, error);
                          }];
}

Сгенерированная CoreData`ой .h файл модели:

#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>

@class Comment, User;

@interface Media : NSManagedObject

@property (nonatomic, getter=isCaptionEdited) BOOL captionEdited;
@property (nonatomic, retain) NSString *clientCacheKey;
@property (nonatomic, retain) NSString *code;
@property (nonatomic, retain) NSNumber *commentsCount;
@property (nonatomic, retain) NSNumber *deviceTimestamp;
@property (nonatomic, retain) NSNumber *filterType;
@property (nonatomic, retain) NSNumber *audioEnable;
@property (nonatomic, getter=isLiked) BOOL liked;
@property (nonatomic, retain) NSNumber *moreComments;
@property (nonatomic, retain) NSNumber *mediaID;
@property (nonatomic, retain) NSNumber *likesCount;
@property (nonatomic, retain) NSNumber *mediaType;
@property (nonatomic, retain) NSNumber *originalHeight;
@property (nonatomic, retain) NSNumber *originalWidth;
@property (nonatomic, getter=isUserPhoto) BOOL userPhoto;
@property (nonatomic, retain) NSNumber *pk;
@property (nonatomic, retain) NSNumber *takenAt;
@property (nonatomic, retain) NSOrderedSet *videoVersion;
@property (nonatomic, retain) NSOrderedSet *imageVersion;
@property (nonatomic, retain) User *user;
@property (nonatomic, retain) Comment *caption;
@property (nonatomic, retain) NSOrderedSet *comments;

@end

@interface Media (CoreDataGeneratedAccessors)

+ (FEMMapping *)defaultMapping;

- (void)insertObject:(NSManagedObject *)value inVideoVersionAtIndex:(NSUInteger)idx;
- (void)removeObjectFromVideoVersionAtIndex:(NSUInteger)idx;
- (void)insertVideoVersion:(NSArray<NSManagedObject *> *)value atIndexes:(NSIndexSet *)indexes;
- (void)removeVideoVersionAtIndexes:(NSIndexSet *)indexes;
- (void)replaceObjectInVideoVersionAtIndex:(NSUInteger)idx withObject:(NSManagedObject *)value;
- (void)replaceVideoVersionAtIndexes:(NSIndexSet *)indexes withVideoVersion:(NSArray<NSManagedObject *> *)values;
- (void)addVideoVersionObject:(NSManagedObject *)value;
- (void)removeVideoVersionObject:(NSManagedObject *)value;
- (void)addVideoVersion:(NSOrderedSet<NSManagedObject *> *)values;
- (void)removeVideoVersion:(NSOrderedSet<NSManagedObject *> *)values;

- (void)insertObject:(NSManagedObject *)value inImageVersionAtIndex:(NSUInteger)idx;
- (void)removeObjectFromImageVersionAtIndex:(NSUInteger)idx;
- (void)insertImageVersion:(NSArray<NSManagedObject *> *)value atIndexes:(NSIndexSet *)indexes;
- (void)removeImageVersionAtIndexes:(NSIndexSet *)indexes;
- (void)replaceObjectInImageVersionAtIndex:(NSUInteger)idx withObject:(NSManagedObject *)value;
- (void)replaceImageVersionAtIndexes:(NSIndexSet *)indexes withImageVersion:(NSArray<NSManagedObject *> *)values;
- (void)addImageVersionObject:(NSManagedObject *)value;
- (void)removeImageVersionObject:(NSManagedObject *)value;
- (void)addImageVersion:(NSOrderedSet<NSManagedObject *> *)values;
- (void)removeImageVersion:(NSOrderedSet<NSManagedObject *> *)values;

- (void)insertObject:(NSManagedObject *)value inCommentsAtIndex:(NSUInteger)idx;
- (void)removeObjectFromCommentsAtIndex:(NSUInteger)idx;
- (void)insertComments:(NSArray<NSManagedObject *> *)value atIndexes:(NSIndexSet *)indexes;
- (void)removeCommentsAtIndexes:(NSIndexSet *)indexes;
- (void)replaceObjectInCommentsAtIndex:(NSUInteger)idx withObject:(NSManagedObject *)value;
- (void)replaceCommentsAtIndexes:(NSIndexSet *)indexes withComments:(NSArray<NSManagedObject *> *)values;
- (void)addCommentsObject:(NSManagedObject *)value;
- (void)removeCommentsObject:(NSManagedObject *)value;
- (void)addComments:(NSOrderedSet<NSManagedObject *> *)values;
- (void)removeComments:(NSOrderedSet<NSManagedObject *> *)values;

@end

Имплиментация модели с маппингом:

#import "Media.h"
#import "Comment.h"
#import "User.h"
#import "VideoVersion.h"

@implementation Media

@dynamic captionEdited;
@dynamic clientCacheKey;
@dynamic code;
@dynamic commentsCount;
@dynamic deviceTimestamp;
@dynamic filterType;
@dynamic audioEnable;
@dynamic liked;
@dynamic moreComments;
@dynamic mediaID;
@dynamic likesCount;
@dynamic mediaType;
@dynamic originalHeight;
@dynamic originalWidth;
@dynamic userPhoto;
@dynamic pk;
@dynamic takenAt;
@dynamic videoVersion;
@dynamic imageVersion;
@dynamic user;
@dynamic caption;
@dynamic comments;

- (BOOL)isLiked
{
    [self willAccessValueForKey:@"liked"];
    BOOL liked = [self primitiveValueForKey:@"liked"];
    [self didAccessValueForKey:@"liked"];

    return liked;
}

- (BOOL)isUserPhoto
{
    [self willAccessValueForKey:@"userPhoto"];
    BOOL userPhoto = [self primitiveValueForKey:@"userPhoto"];
    [self didAccessValueForKey:@"userPhoto"];

    return userPhoto;
}

- (BOOL)isCaptionEdited
{
    [self willAccessValueForKey:@"captionEdited"];
    BOOL captionEdited = [self primitiveValueForKey:@"captionEdited"];
    [self didAccessValueForKey:@"captionEdited"];
    return captionEdited;
}

+ (NSDictionary *)elementToPropertyMappings {
    return @{
             @"caption_is_edited" : @"captionEdited",
             @"client_cache_key" : @"clientCacheKey",
             @"code" : @"code",
             @"comment_count" : @"commentsCount",
             @"device_timestamp" : @"deviceTimestamp",
             @"filter_type" : @"filterType",
             @"has_audio" : @"audioEnable",
             @"has_liked" : @"liked",
             @"has_more_comments" : @"moreComments",
             @"like_count" : @"mediaID",
             @"original_height" : @"originalHeight",
             @"original_width" : @"originalWidth",
             @"photo_of_you" : @"userPhoto",
             @"pk" : @"pk",
             @"taken_at" : @"takenAt",
             };
}

+ (FEMMapping *)defaultMapping {
    FEMMapping *mapping = [[FEMMapping alloc] initWithEntityName:@"Media"];
    [mapping addAttributesFromDictionary:[Media elementToPropertyMappings]];
    [mapping addRelationshipMapping:[Comment defaultMapping] forProperty:@"caption" keyPath:@"caption"];
    [mapping addRelationshipMapping:[User defaultMapping] forProperty:@"user" keyPath:@"user"];
    [mapping addToManyRelationshipMapping:[VideoVersion defaultMapping] forProperty:@"videoVersion" keyPath:@"video_versions"];
    [mapping addToManyRelationshipMapping:[Comment defaultMapping] forProperty:@"comments" keyPath:@"comments"];
    return mapping;
}

@end

По факту все работает и ниодной ошибки в логе нет, но при выводе объекта в лог получается:

<Media: 0x7fd509de9640> (entity: Media; id: 0x7fd509d4feb0 <x-coredata:///Media/tF63F01BE-D0D8-4978-8BDE-913E888F1B7F2> ; data: {
    audioEnable = nil;
    caption = "0x7fd50c4ce4d0 <x-coredata:///Comment/tF63F01BE-D0D8-4978-8BDE-913E888F1B7F3>";
    captionEdited = 0;
    clientCacheKey = nil;
    code = 8EXx8gQysz;
    comments =     (
    );
    commentsCount = 0;
    deviceTimestamp = 0;
    filterType = 0;
    imageVersion =     (
    );
    liked = 1;
    likesCount = 0;
    mediaID = 0;
    mediaType = 0;
    moreComments = nil;
    originalHeight = 0;
    originalWidth = 0;
    pk = "-2147483648";
    takenAt = 0;
    user = "0x7fd509de9d40 <x-coredata:///User/tF63F01BE-D0D8-4978-8BDE-913E888F1B7F5>";
    userPhoto = 0;
    videoVersion =     (
    );
})

То есть некоторые объекты маппятся верно, некоторые вообще не маппятся, третьи представляют из себя отрицательные числа.
Подскажите, пожалуйста, что я делаю не так и как мне исправить? Заранее спасибо.

Error on non existing relationship

I've got a 'Person' entity with a relationship to a 'Location' entity. But in some cases a 'Person' entity can exists without a 'Location' entity. That's why my JSON model doesn't provide a 'location' key for certain persons.

When I try to deserialize a JSON 'Person' into a ManagedObject without a given location relationship, I get the following error:

*** Assertion failure in -[FEMCache addExistingObject:usingMapping:], /.../FEMCache.m:185
2014-09-08 15:30:47.906 AppName[7866:3191678] 
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'No value for key (locationId) on object (<Location: 0x1782d8330> (entity: Location; id: 0x178826480 <x-coredata:///Location/t149B2931-DA62-4EFC-8C7D-3CFC8058F57A16> ; data: {
city = nil;
country = nil;
houseNumber = nil;
latitude = nil;
locationId = nil;
longitude = nil;
relatedParkingSlot = nil;
streetName = nil;
zipCode = nil;
})) found'

Any suggestions on this?

Mapping NSString to NSNumber if it possible

because values in json may wrap by " " and NSJSONSerialization will return something like this:

"value_aosina" = "-2758920";
"value_chdspood" = 5390621;
"value_chpzp" = "-1812150";

Invalid parameter not satisfying: primaryKeyMapping

Hi, I just don't understand what the problem is, I'll try to explain.

I have an entity which has 4 attributes: key, locale, translation. In a traditional database, the primary key would be created with two fields, key and locale.

In core data, having no need for a primary key, it's not clear which primaryKey should I set in

+ (FEMManagedObjectMapping *)localizableTextMapping{
return [FEMManagedObjectMapping mappingForEntityName:@"LocalizableText" configuration:^(FEMManagedObjectMapping *mapping) {

    [mapping setPrimaryKey:@"key"];  // object uniquing

    [mapping addAttributeMappingFromArray:@[@"locale",@"translation"]];

}];

}

What am I missing/doing wrong?

Thanks

Mapping ISO Date

Hello,
I am having problem with mapping of ISO Date.
Date I get from server is 2016-03-07T00:00:00.000-06:00

And when this reaches to this function:

id FEMRepresentationValueForAttribute(id representation, FEMAttribute *attribute)
{
id value = attribute.keyPath ? [representation valueForKeyPath:attribute.keyPath] : representation;
// nil is a valid value for missing keys. therefore attribute is discarded
if (value != nil) {
// if by mistake nil returned we still have to map it to the NSNull to indicate missing value
return [attribute mapValue:value] ?: [NSNull null];
}
return value;
}

Here, [attribute mapValue:value] is returning nil. Due to which none of my dates are saved into coredata.

Can you help out how can I get this worked.

Recursive mapping

Hi! Thx for your work on FEM.

I have faced a problem with one case. Let's say, we have entity with a relationship to self.

TreeNode {
id - nsnumber
name - nsstring
children - nsset<TreeNode>
}

I define mapping, as it was recommended - having a defaultMapping method, which returns FEMMapping.
And I have an infinite recursion, because inside defautlMapping I call this method again to get mapping for children property.
And of course, I'm talking about a general situation - when number of tree levels are not defined or known while developing an app.

Any ideas, how to handle this?
Or, maybe, any ideas about what kind of changes should I do in FEM to make fork / pull request?

Quick start problem

Hello, I am having trouble setting ap FEM.

This is my code:

@implementation MappingProvider

+ (FEMManagedObjectMapping *)chromosomeMapping {
    return [FEMManagedObjectMapping mappingForEntityName:@"Chromosome" configuration:^(FEMManagedObjectMapping *mapping) {

    [mapping setPrimaryKey:@"chromosomeID"];  // object uniquing


    //[mapping addRelationshipMapping:[self carMapping] forProperty:@"car" keyPath:@"car"];
    [mapping addToManyRelationshipMapping:[self geneMapping] forProperty:@"genes" keyPath:@"genes"];
}];
}

+ (FEMManagedObjectMapping *)mutationMapping {
    return [FEMManagedObjectMapping mappingForEntityName:@"Mutation" configuration:^(FEMManagedObjectMapping *mapping) {
    [mapping setPrimaryKey:@"mutationID"];

    //[mapping addAttributeMappingFromArray:@[@"model", @"year"]];
    }];
}

+ (FEMManagedObjectMapping *)geneMapping {
    return [FEMManagedObjectMapping mappingForEntityName:@"Gene" configuration:^(FEMManagedObjectMapping *mapping) {
       [mapping setPrimaryKey:@"geneID"];
    //[mapping addAttributeMappingFromArray:@[@"number", @"ddd", @"ddi"]];
        [mapping addToManyRelationshipMapping:[self mutationMapping] forProperty:@"variations" keyPath:@"variations"];
    }];
}

@end`

And how I call it:

Chromosome *c = [FEMManagedObjectDeserializer deserializeObjectExternalRepresentation:chr usingMapping:[MappingProvider chromosomeMapping] context:_setupContext];

I have this error

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid parameter not satisfying: primaryKeyMapping'
*** First throw call stack:
(
0 CoreFoundation 0x000000010dbca495 __exceptionPreprocess + 165
1 libobjc.A.dylib 0x000000010d92499e objc_exception_throw + 43
2 CoreFoundation 0x000000010dbca31a +[NSException raise:format:arguments:] + 106
3 Foundation 0x000000010d4bbf19 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 189
4 Exome Scan 0x000000010c8676a2 -[FEMCache inspectObjectRepresentation:usingMapping:] + 450

Any idea?

Incorrect datetime mapping

Mapping done with usage of TimeZone based on Europe/London. It leads to usage of BST at summer instead of UTC.

(De-)Serialize inner properties

I have JSON with structure:

{
  desc: "Some description",
  image: {
    url: "http://server.com/path",
    size: {width: 800, height: 600}
  }
}

And object

@interface Model : NSObject

@property (nonatomic, strong) NSString *desc;

@property (nonatomic, strong) NSURL *image_url;

@property (nonatomic) int image_width;
@property (nonatomic) int image_height;

@end

Can I serialize/deserialize Model with FastEasyMapping?

Reference to ‘FEMManagedObjectMapping’ is ambiguous

Hello!
I’m getting error “Reference to ‘FEMManagedObjectMapping’ is ambiguous” when building my project using version 1.0.1. Version 0.5.1 i have used before works fine. Any ideas what am i doing wrong?
Thanks!

Mapping NSNumber to NSString

I have a property of type NSString and a json value that is NSNumber. The json value should be translated to the property type.

One of possible implementations:

- (void)emk_setValueIfDifferent:(id)value forKey:(NSString *)key {

    id _value = [self valueForKey:key];

    if ([value isKindOfClass:NSNumber.class]) {
        objc_property_t property = class_getProperty([self class], [key UTF8String]);
        NSString *type = FEMPropertyTypeStringRepresentation(property);

        if ([NSClassFromString(type) isSubclassOfClass:NSString.class]) {
            value = [(NSNumber *)value stringValue];
        }
    }

    if (_value != value && ![_value isEqual:value]) {
        [self setValue:value forKey:key];
    }
}

Also, we need NSString -> NSNumber...

Ability to disable autocreation properties

Now each property/relationship autocreated even if there are missing such json key in response. For relationship there are exists similar option like "weak", but it's has another meaning.
Could you add in mapping property like "disableAutocreation" to exclude creation empty object if there are missing key in json(during looking in the code is should involve on "allocateIfNeeded" in FEMDeserializer). Also as additional option I could use "map:".

Carthage

Add explicit version for carthage

How do you parse ?

Hi,

How would you parse :

{
    "uuid": "bbb",
    "comment": "Hi hello",
    "username": "Alex",
    "likers_uuids": ["xxx", "yyy", "zzz"],
    "post_uuid: "hhh"
}

Comment.swift

class Comment: BaseObject {
    var username:String
    var commentDescription: String
    var likers: [User]
    var post: Post

    override class func defaultMapping() -> FEMMapping {
        let mapping = super.defaultMapping() // parsing uuid in super class

        mapping.addAttributesFromDictionary(["commentDescription": "comment"])

     // Line below is wrong because likers_uuids  doesn't deliver an object {"uuid" : "ggg", "username": "Patrick"} put directly an array of String containing uuids
        mapping.addToManyRelationshipMapping(User.defaultMapping(), forProperty: "likers", keyPath: "likes_uuids")
    // Neither this one because post_uuid doesn't deliver an object {"uuid": "eee", "create_date": 12345} but the uuid of the post to relate to
        mapping.addToManyRelationshipMapping(Post.defaultMapping(), forProperty: "post", keyPath: "post_uuid")
        return mapping
    }
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.