A tool for fast serializing & deserializing of JSON

Overview

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.

Comments
  • Synchronise local and server id

    Synchronise local and server id

    I have the following (simplified) model:

    class Message {
        let id: String
        let clientId: String
    }
    

    When sending message, client creates Message object and generates clientId. When our server receives this request, it generates actual Message id. Both are returned by server to client.

    If we use id as primaryKey in mapping, we will end up with duplicated message. We can't use clientId as primaryKey, because messages that are not generated on current device don't have one.

    Is there any workaround for this problem?

    One option that could be possible is to have dynamic primaryKey. While mapping, mapper would ask to provide primaryKey based on payload of object being mapped. With that, we could determine if we need to use clientId or id.

    opened by jcavar 25
  • Types introspection refactoring / improvement

    Types introspection refactoring / improvement

    Yuri Kotov simplified types introspection via usage of already existing runtime api instead of custom type check. Removed boxing of scalar return types.

    enhancement 
    opened by dimazen 18
  • Error on non existing relationship

    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?

    bug 
    opened by Ma-He 15
  • Using FEMMapping as NSMapTable key leads to duplicated objects

    Using FEMMapping as NSMapTable key leads to duplicated objects

    Hi, It new version, FEM uses FEMMapping instead entity name as before as NSMapTable key. When using recursive relationships and therefore different FEMMapping in child object and when processing child, it tries to get object from cache but since child mapping is different it can't get it and inserts new object.

    Simple solution is I think just to use mapping.entityName as key.

    opened by jcavar 13
  • Crash on array [NSKeyedArchiver archivedDataWithRootObject:array]

    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?

    question 
    opened by MattiaConfalonieri 12
  • Quick start problem

    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?

    question 
    opened by superandrew 11
  • Crash using relationship binding by PK

    Crash using relationship binding by PK

    The library crashes when the object which is being binding using a PK has a relationship called equal to other property that the parent object. Example:

    Example of Project JSON { "name": "Test", "id": 19, "membership": { ... }, "user_group_id": 5 }

    Example of UserGroup JSON { "name": "Test", "id": 5 "membership": { ... }, "user_group_id": 5 }

    Both object have a relationship called "membership" so when the library is doing the binding using the property "user_group_id" fails.

    opened by asam139 9
  • Best way to track changes

    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?

    enhancement 
    opened by k06a 9
  • nil in relationships

    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

    opened by oks 9
  • Parsing Array

    Parsing Array

    Hello,

    i am parsing following: reason: 'Unacceptable type of value for ordered to-many relationship: property = "visitStatusList"; desired type = NSOrderedSet; given type = __NSArrayM; value = ( "<VisitStatus: 0x7c64fec0> (entity: VisitStatus; id: 0x7c64ff20 x-coredata:///VisitStatus/tB60DAD31-2D22-4158-B2B3-4D27C5F75A763 ; data: {\n conclusion = "";\n createdBy = 18;\n createdByName = "Manager Raina";\n createdTime = "Jul 11, 2017 11:29:05 AM";\n iD = 729;\n interactionTime = "Jul 11, 2017 11:28:00 AM";\n interactionTypeID = 1;\n interactionWith = 0;\n interactionWithName = "";\n nextActionID = 1;\n nextActionTime = "Jul 12, 2017 4:30:00 AM";\n plannVisit = nil;\n visitID = 1360;\n visitOutcomeID = 1;\n visitTypeID = 1;\n})", "<VisitStatus: 0x7c650cd0> (entity: VisitStatus; id: 0x7c005f20 x-coredata:///VisitStatus/tB60DAD31-2D22-4158-B2B3-4D27C5F75A764 ; data: {\n conclusion = "";\n createdBy = 19;\n createdByName = "Admin Lee";\n createdTime = "Aug 1, 2017 12:42:25 PM";\n iD = 739;\n interactionTime = "Aug 1, 2017 12:42:00 PM";\n interactionTypeID = 1;\n interactionWith = 0;\n interactionWithName = "";\n nextActionID = 1;\n nextActionTime = "Aug 1, 2017 2:30:00 PM";\n plannVisit = nil;\n visitID = 1360;\n visitOutcomeID = 1;\n visitTypeID = 1;\n})" ).'

    thanks in advance.....

    opened by vishaldeshai 8
  • Is there any JSON Mapping faster than FastEasyMapping?

    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?

    question 
    opened by steve21124 8
  • Getting value nill after mapping

    Getting value nill after mapping

    class of mapping is :: class XYZSetting: NSManagedObject {

    var address_id:NSNumber!
    var client_name:String!
    var clientvendorid:NSNumber!
    var company_id:NSNumber!
    var location_type:NSNumber!
    var modified_by:NSNumber!
    var user_id:NSNumber!
    var userlocationtype:NSNumber!
    var clientaddress:AddressList?
    
    
    class func entityName()->String{
        return "XYZSetting"
    }
    
    class func defaultMapping()->FEMMapping{
        let mapping = FEMMapping.init(entityName: self.entityName())
        mapping.addAttribute(Mapping.intAttributeFor(property: "user_id", keyPath: "UserID"))
        //mapping.addAttribute(Mapping.intAttributeFor(property: <#T##String#>, keyPath: <#T##String#>))
        mapping.addAttribute(Mapping.intAttributeFor(property: "company_id", keyPath: "CompanyID"))
        mapping.addAttribute(Mapping.intAttributeFor(property: "address_id", keyPath: "AddressID"))
        mapping.addAttribute(Mapping.intAttributeFor(property: "clientvendorid", keyPath: "ClientVendorID"))
         mapping.addAttribute(Mapping.intAttributeFor(property: "location_type", keyPath: "LocationType"))
        mapping.addAttribute(Mapping.intAttributeFor(property: "modified_by", keyPath: "ModifiedBy"))
        mapping.addAttribute(Mapping.intAttributeFor(property: "user_location_type", keyPath: "UserLocationType"))
        mapping.addAttributes(from: ["client_name":"ClientName"])
    mapping.addRelationshipMapping(AddressList.defaultmapping(), forProperty: "clientaddress", keyPath: "ClientAddress")
        mapping.primaryKey = "clientvendorid"
        return mapping
    }
    class func getAll()->[DefaultSetting]{
        return XYZSetting.mr_findAll() as? [XYZSetting] ?? [XYZSetting]()
    }
    class func getDefaultSetting()->XYZSetting?{
    
        if let dsetting = XYZSetting.mr_findFirst(in: NSManagedObjectContext.mr_default()) as? XYZSetting{ 
           {
            if let defaultsetting = XYZSetting.mr_findAll()?.first as? DefaultSetting{
                print("address id = \(defaultsetting.address_id)")
                //print(defaultsetting.userid)
                print("Location  type = \(defaultsetting.location_type)")
                }
            print("location type in model  = \(dsetting.location_type)")
           return dsetting
        }
    

    else{ return nil } } }

    code for mapping is ::

    MagicalRecord.save({ (localcontext) in XYZSetting.mr_truncateAll(in: localcontext) print("count of default setting is = (XYZSetting.getAll().count)") let arr = FEMDeserializer.collection(fromRepresentation: [dic], mapping: XYZSetting.defaultMapping(), context: localcontext) print("setting arr = (arr)") print("count of default setting after mapping (XYZSetting.getAll().count)")

    localcontext.mr_save({ (localcontext) in print("saving") }, completion: { (status, error) in print("status = (status)") print("saved") print("get default setting (XYZSetting.getDefaultSetting())") print("Location type using default = (XYZSetting.getDefaultSetting()?.location_type)") if let defaultsetting = XYZSetting.mr_findAll()?.first as? DefaultSetting{ print("address id = (defaultsetting.address_id)") //print(defaultsetting.userid) print("Location type = (defaultsetting.location_type)") } })

    Getting log is this :: " count of default setting is = 1 setting arr = [<DefaultSetting: 0x600000e28a00> (entity: DefaultSetting; id: 0x600003f44d80 x-coredata:///DefaultSetting/t1E3B2DA0-7EED-4FE9-B738-B4630920C75D2; data: { "address_id" = 0; "client_name" = nil; clientaddress = "0x96cd7ec8fb807514 x-coredata://AEB29A34-BE29-4A08-9966-E497846108D9/AddressList/p318"; clientvendorid = 0; "company_id" = 1604; "location_type" = 4; "modified_by" = 0; "user_id" = 12973; "user_location_type" = 1; })] count of default setting after mapping 1 2020-12-10 11:43:28.442586+0530 SuperSales[15152:97995] Created new private queue context: <NSManagedObjectContext: 0x60000047c000> saving 2020-12-10 11:43:28.443971+0530 SuperSales[15152:97995] → Saving <NSManagedObjectContext (0x60000047d860): saveWithBlock:completion:> on a background thread status = false saved address id = nil Location type in dafault setting = nil location type in model = nil get default setting Optional(<DefaultSetting: 0x600000e34d20> (entity: DefaultSetting; id: 0x96cd7ec8ff707516 x-coredata://AEB29A34-BE29-4A08-9966-E497846108D9/DefaultSetting/p2; data: )) address id = nil Location type in dafault setting = nil location type in model = nil Location type using default = nil address id = nil Location type = nil

    "

    opened by krishnasolankiBigbang 0
  • Getting crash App While Mapping

    Getting crash App While Mapping

    Hello I am using swift verison pod 'FastEasyMapping', '~> 1.2'.

    I am getting error :: Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<__NSCFString 0x6000001d97e0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key ID Getting exception in line FEMDeserializer.collection(fromRepresentation: arrPlanVisit, mapping: ABC.defaultmapping(), context: localContext)

    class of ABC is :: import UIKit

    @objc(ABC) class ABC: NSManagedObject { // @NSManaged var ID: Int64

    class func defaultmapping()->FEMMapping{ let mapping = FEMMapping.init(entityName: "ABC") mapping.addAttribute(Mapping.intAttributeFor(property: "iD", keyPath: "ID")) return mapping }

    Response from Server ::

    • key : "ABC" ▿ value : 10 element ▿ 8 : 2 elements - key : ID - value : 1641 @dimazen any solution for this ?
    opened by krishnasolankiBigbang 0
  • Swift Package Manager Support

    Swift Package Manager Support

    Hello, There are some changes in our project that will require FEM to be integrated using SPM. I managed to get this working in my fork but wanted to discuss solution before creating PR.

    SPM has publicHeadersPath option where you can specify location of your headers. Unfortunately, it seems that it doesn't support wildcards and supports only one location. As FEM's headers are distributed in different directories, this causes problems.

    I think we have 2 options:

    • Move everything under Source and remove directories (we can still have groups in Xcode)
    • Move all headers under Source/Headers and keep existing directory structure

    What are your thoughts?

    opened by jcavar 1
  • IN Operator with no sorting

    IN Operator with no sorting

    I have menu entity and i want to fetch menus list as per the MenuID listing.

    i am passing manu ids using 'IN' operator but it is automatically sorting but i need to prevent sorting.(simply needs menus list without sorting i.e list as per giving menu id list with 'IN' operator')

    thanks in advance.

    opened by vishaldeshai 0
  • is FastEasyMapping ready for swift 4?

    is FastEasyMapping ready for swift 4?

    I have iOS project with swift 3.x, and trying to migrate it to latest swift 4.x I have setuped FastEasyMapping via Cocoapods like this: pod 'FastEasyMapping', :git => 'https://github.com/Yalantis/FastEasyMapping.git', :branch => 'required-pk'

    But I have next crash issue when try to serialize/deserialize objects: Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<fasteasymappingtest.SGSignInEntity 0x604000151b40> valueForUndefinedKey:]: this class is not key value coding-compliant for the key email.' *** First throw call stack: ( 0 CoreFoundation 0x000000010e6dc1e6 __exceptionPreprocess + 294 1 libobjc.A.dylib 0x000000010afb1031 objc_exception_throw + 48 2 CoreFoundation 0x000000010e6dc0b9 -[NSException raise] + 9 3 Foundation 0x000000010aa9ef58 -[NSObject(NSKeyValueCoding) valueForUndefinedKey:] + 226 4 Foundation 0x000000010a9caebc -[NSObject(NSKeyValueCoding) valueForKey:] + 284 5 FastEasyMapping 0x00000001098533fc -[FEMSerializer setValueOnRepresentation:fromObject:withFieldMapping:] + 156 6 FastEasyMapping 0x000000010985295f -[FEMSerializer _serializeObject:usingMapping:] + 463 7 FastEasyMapping 0x0000000109852c92 -[FEMSerializer serializeObject:usingMapping:] + 114 8 FastEasyMapping 0x0000000109853fe0 +[FEMSerializer(Shortcut) serializeObject:usingMapping:] + 144

    In this method: - (void)setValueOnRepresentation:(NSMutableDictionary *)representation fromObject:(id)object withFieldMapping:(FEMAttribute *)fieldMapping { id returnedValue = [object valueForKey:fieldMapping.property]; if (returnedValue || self.includeNulls) { returnedValue = [fieldMapping reverseMapValue:returnedValue] ?: [NSNull null]; [self setValue:returnedValue forKeyPath:fieldMapping.keyPath inRepresentation:representation]; } }

    object can not return valueForKey, because doesn't casted to valid class. I think it's some changes in Swift 4 and Objective-C relation.

    Please provide any advise how to fix it? I am using your library in many places, and can not rewrite it to other libraries easy.

    opened by surkis 3
  • Primary key not required

    Primary key not required

    Hello!

    I understand data validation is more of general concern and maybe out of scope for this librarty. There were also few questions asked before, however I am interested specifically about primaryKey attribute.

    If you specify it for your mapping, does it make sense to create object when not in representation? At the moment framework just checks and if not in, it ignores it, but it sounds to me like this should result in error (or nil).

    What are your thoughts?

    opened by jcavar 4
Releases(1.2.2)
  • 1.2.2(Jun 25, 2017)

  • 1.2.1(May 20, 2017)

    This release introduces:

    • More tests

    API breaking changes

    • Changed signature on FEMObjectStore: from - beginTransaction:(nullable NSMapTable<FEMMapping *, NSSet<id> *> *) to - beginTransaction:(nullable NSDictionary<NSNumber *, NSSet<id> *> *) This shouldn't affect your code unless you're running your own store.

    Bugfixes

    • Fixes https://github.com/Yalantis/FastEasyMapping/issues/96
    Source code(tar.gz)
    Source code(zip)
    FastEasyMapping.framework.zip(3.58 MB)
  • 1.2(May 10, 2017)

    Realm support, bugfixes and more

    This release introduces:

    • Realm support!
    • Add support for macOS, tvOS, watchOS as dynamic frameworks
    • Add support for Carthage (#56)
    • Add ability to determine whether FEMRelationship recursive or not
    • Add tests for Swift
    • Allowed to add plain FEMRelationship to the same mapping (recursive) without using add(ToMany)RecursiveRelationshipMapping(_:forProperty:keyPath:)
    • Add benchmarks for performance analyze

    API breaking changes

    • Rework of the FEMObjectStore API to support Realm. FEMObjectStore no longer parses raw JSON in order to get primary keys - this part done by the FEMDeserializer. Also refactoring includes names alignment
    • Refactoring of the internal FEMManagedObjectCache into a generic FEMObjectCache. FEMObjectCache also no longer parses JSON
    • Renamed FEMMapping.add(toManyRelationshipMapping:forProperty:keyPath:) to FEMMapping.addToManyRelationshipMapping(_:forProperty:keyPath:) (Swift only)
    • Removed deprecated headers: FEMObjectDeserializer and FEMManagedObjectDeserializer (replaced by FEMDeserializer); FEMObjectMapping and FEMManagedObjectMapping (replaced by FEMMapping)
    • Removed deprecated methods on: FEMDeserializer, FEMMapping, FEMRelationship
    • Replace internal unused functions FEMMappingCollectUsedEntityNames and FEMMappingApply by -[FEMMapping flatten] and FEMRepresentationCollectPresentedPrimaryKeys

    Bugfixes

    • Fixed recursive relationship to same object: #81
    • Fixed leak of recursive relationships: #83
    • Temporary fix for #80
    Source code(tar.gz)
    Source code(zip)
    FastEasyMapping.framework.zip(3.56 MB)
  • 1.1.2(Apr 3, 2017)

    This release introduces:

    • None

    API breaking changes

    • None

    Bugfixes

    • Fixes https://github.com/Yalantis/FastEasyMapping/issues/81: now objects gets registered to the store before relationships deserialization
    • Minor typo fixes
    Source code(tar.gz)
    Source code(zip)
  • 1.1.1(Sep 29, 2016)

    This release introduces:

    • Inline documentation for most of the public APIs https://github.com/Yalantis/FastEasyMapping/issues/69. Now APIs have a rich comments and useful examples explaining how to use, etc.
    • NSCopying support for easier reuse of mappings by subclasses: https://github.com/Yalantis/FastEasyMapping/pull/74

    API breaking changes

    • None

    Bugfixes

    • None
    Source code(tar.gz)
    Source code(zip)
  • 1.1(Aug 29, 2016)

    • Merge recursive relationships PR https://github.com/Yalantis/FastEasyMapping/pull/54
    • Update nullability specifiers including minor fixes https://github.com/Yalantis/FastEasyMapping/pull/70
    • Fixed Travis CI config https://github.com/Yalantis/FastEasyMapping/issues/63
    • Update testing CocoaPods to 1.0.1 version
    • Fixed https://github.com/Yalantis/FastEasyMapping/issues/47
    Source code(tar.gz)
    Source code(zip)
  • 1.0.2(Jan 26, 2016)

  • 1.0.1(Oct 26, 2015)

    • Add tvOS support in podspec: https://github.com/Yalantis/FastEasyMapping/pull/39
    • Fix warnings "Method override for the designated initializer of the superclass '-init' not found"
    Source code(tar.gz)
    Source code(zip)
  • 1.0(Sep 1, 2015)

    • Nullability support for easier Swift integration.
    • Replacing of FEMObjectDeserializer and FEMManagedObjectDeserializer by FEMDeserializer. Backward compatible via @compatibility_alias.
    • Replacing of FEMObjectMapping and FEMManagedObjectMapping by FEMMapping. Backward compatible via @compatibility_alias.
    • Added Delegation for FEMDeserializer.
    • FEMDeserializer supports weak relationships.
    • Fixed bug when nil value for custom FEMAttribute map is ignored during deserialization.
    • Fixed bug when nil value for custom FEMAttribute reverse map is ignored during serialization.
    • Refactoring of internals.
    • Numerious renaming in favour of shorter method names. Methods from 0.5.1 marked as deprecated.
    • Update tests and examples to latest syntax.
    • Add full description to README.
    Source code(tar.gz)
    Source code(zip)
  • 0.4.1(Sep 1, 2015)

  • 0.3.8(Sep 1, 2015)

  • 0.3.7(Sep 1, 2015)

Owner
Yalantis
Knowledge is power and the way to get power is by sharing knowledge. We are open source because this is a smart way to live, work and play.
Yalantis
A fast, convenient and nonintrusive conversion framework between JSON and model. Your model class doesn't need to extend any base class. You don't need to modify any model file.

MJExtension A fast, convenient and nonintrusive conversion framework between JSON and model. 转换速度快、使用简单方便的字典转模型框架 ?? ✍??Release Notes: more details Co

M了个J 8.5k Jan 3, 2023
JSEN (JSON Swift Enum Notation) is a lightweight enum representation of a JSON, written in Swift.

JSEN /ˈdʒeɪsən/ JAY-sən JSEN (JSON Swift Enum Notation) is a lightweight enum representation of a JSON, written in Swift. A JSON, as defined in the EC

Roger Oba 8 Nov 22, 2022
JSON-Practice - JSON Practice With Swift

JSON Practice Vista creada con: Programmatic + AutoLayout Breve explicación de l

Vanesa Giselle Korbenfeld 0 Oct 29, 2021
Ss-json - High-performance json parsing in swift

json 0.1.1 swift-json is a pure-Swift JSON parsing library designed for high-per

kelvin 43 Dec 15, 2022
Swift-json - High-performance json parsing in swift

json 0.1.4 swift-json is a pure-Swift JSON parsing library designed for high-per

kelvin 43 Dec 15, 2022
Swift parser for JSON Feed — a new format similar to RSS and Atom but in JSON.

JSONFeed Swift parser for JSON Feed — a new format similar to RSS and Atom but in JSON. For more information about this new feed format visit: https:/

Toto Tvalavadze 31 Nov 22, 2021
AlamofireObjectMappe - An Alamofire extension which converts JSON response data into swift objects using ObjectMapper

AlamofireObjectMapper An extension to Alamofire which automatically converts JSON response data into swift objects using ObjectMapper. Usage Given a U

Tristan Himmelman 2.6k Dec 29, 2022
Functional JSON Parser - Linux Ready

Functional JSON Parser Feature Linux Ready Type-safe JSON parsing Functional value transformation Easy to parse nested value Dependency free No define

Ryo Aoyama 117 Sep 9, 2022
Argo is a library that lets you extract models from JSON or similar structures in a way that's concise, type-safe, and easy to extend

Argo is a library that lets you extract models from JSON or similar structures in a way that's concise, type-safe, and easy to extend. Using Argo

thoughtbot, inc. 3.5k Dec 20, 2022
Arrow 🏹 Parse JSON with style

Arrow ?? Parse JSON with style

Fresh 376 Nov 8, 2022
Elevate is a JSON parsing framework that leverages Swift to make parsing simple, reliable and composable

Elevate is a JSON parsing framework that leverages Swift to make parsing simple, reliable and composable. Elevate should no longer be used for

Nike Inc. 611 Oct 23, 2022
Reflection based (Dictionary, CKRecord, NSManagedObject, Realm, JSON and XML) object mapping with extensions for Alamofire and Moya with RxSwift or ReactiveSwift

EVReflection General information At this moment the master branch is tested with Swift 4.2 and 5.0 beta If you want to continue using EVReflection in

Edwin Vermeer 964 Dec 14, 2022
Freddy - A reusable framework for parsing JSON in Swift.

Why Freddy? Parsing JSON elegantly and safely can be hard, but Freddy is here to help. Freddy is a reusable framework for parsing JSON in Swift. It ha

Big Nerd Ranch 1.1k Jan 1, 2023
[Deprecated] A shiny JSON parsing library in Swift :sparkles: Loved by many from 2015-2021

?? Deprecation Notice ?? Gloss has been deprecated in favor of Swift's Codable framework. The existing Gloss source is not going away, however updates

Harlan Kellaway 1.6k Nov 24, 2022
HandyJSON is a framework written in Swift which to make converting model objects to and from JSON easy on iOS.

HandyJSON To deal with crash on iOS 14 beta4 please try version 5.0.3-beta HandyJSON is a framework written in Swift which to make converting model ob

Alibaba 4.1k Dec 29, 2022
Himotoki (紐解き) is a type-safe JSON decoding library written purely in Swift.

Himotoki Himotoki (紐解き) is a type-safe JSON decoding library written purely in Swift. This library is highly inspired by the popular Swift JSON parsin

IKEDA Sho 799 Dec 6, 2022
JASON is a faster JSON deserializer written in Swift.

JASON is a faster JSON deserializer written in Swift. JASON is the best framework we found to manage JSON at Swapcard. This is by far the fastest and

Damien 1k Oct 15, 2022
JSONHelper - ✌ Convert anything into anything in one operation; JSON data into class instances, hex strings into UIColor/NSColor, y/n strings to booleans, arrays and dictionaries of these; anything you can make sense of!

JSONHelper Convert anything into anything in one operation; hex strings into UIColor/NSColor, JSON strings into class instances, y/n strings to boolea

Baris Sencan 788 Jul 19, 2022
ObjectMapper is a framework written in Swift that makes it easy for you to convert your model objects to and from JSON.

ObjectMapper is a framework written in Swift that makes it easy for you to convert your model objects (classes and structs) to and from J

Tristan Himmelman 9k Jan 2, 2023