📜 ⬆️ ⬇️

EasyMapping, or JSON Journey

Computer programs are the most complex things that humans make. It is also a program of life to the program. If you can read it, then you can.

Douglas Crockford, JSON Specification Author


JSON is a bridge between two worlds: the world of web services and the world of client applications. However, the bridge is not so perfect that the data existed in the same format. So far, we are always forced to convert information into a representation of the language with which we work, for the architecture of the application we are writing. For such a transformation to be successful, it must first be simple.
')
There are many ways to turn JSON into Objective-C objects, but many of them have their own drawbacks that prevent them from working with them. There are well-known and beloved by many RestKit , however, unfortunately, it works effectively only if there is an ideal REST API. Step aside - and you will hammer nails with a microscope, not understanding why you need to write such complex structures for fairly simple things. There is a solution from the developers of GitHub - Mantle , however with it you will be forced to inherit from the base class Mantle and constantly use NSValueTransformer - not the most popular technology in iOS / Mac OS development.

I want to talk about the framework, which was recently found on the open spaces of GitHub, and which allows you to quite simply and beautifully convert JSON to Objective-C objects - EasyMapping .

If you are interested, welcome under the cat!

Task


Take for example the following JSON:

{ "name": "Lucas", "email": "notexisting@gmail.com", "gender" : "male", "car": { "model": "i30", "year": "2013" }, "phones": [ { "ddi": "55", "ddd": "85", "number": "1111-1111" }, { "ddi": "55", "ddd": "11", "number": "2222-222" } ] } 


In Objective-C, we will use the following classes:

 typedef enum { GenderMale, GenderFemale } Gender; @interface Person : NSObject @property (nonatomic, copy) NSString *name; @property (nonatomic, copy) NSString *email; @property (nonatomic, assign) Gender gender; @property (nonatomic, strong) Car *car; @property (nonatomic, strong) NSArray *phones; @end @interface Car : NSObject @property (nonatomic, copy) NSString *model; @property (nonatomic, copy) NSString *year; @end @interface Phone : NSObject @property (nonatomic, copy) NSString *DDI; @property (nonatomic, copy) NSString *DDD; @property (nonatomic, copy) NSString *number; @end 


Implementation


For each class, you need to create a mapping (EKObjectMapping), for example:

 EKObjectMapping * mapping = [[EKObjectMapping alloc] initWithObjectClass:[Person class]]; 


After that, it is necessary to add key-value relationships for the connection <key in JSON> -> <object property name>. Consider the most common situations in mapping and handling of these situations using EasyMapping.

1. The keys in JSON match the property names of the object.

Example, Person class:

 [mapping mapFieldsFromArray:@[@"name",@"email"]]; 


2. Keys in JSON are different from object property names

Example, Phone class:

 [mapping mapFieldsFromDictionary:@{@"ddi":@"DDI", @"ddd":@"DDD"}]; 


3. Value requires additional processing.

Example, Person class

 NSDictionary *genders = @{ @"male": @(GenderMale), @"female": @(GenderFemale) }; [mapping mapKey:@"gender" toField:@"gender" withValueBlock:^(NSString *key, id value) { return genders[value]; }]; 


4. Object contains another class object.

Example, Person class

 [mapping hasOneMapping:[Car objectMapping] forKey:@"car"]; 


5. The object contains an array of objects of another class.

Example. Person class

 [mapping hasManyMapping:[Phone objectMapping] forKey:@"phones"]; 


Result


Fully mappings for our classes:

 // Car mapping + (EKObjectMapping *)objectMapping { EKObjectMapping * mapping = [[EKObjectMapping alloc] initWithObjectClass:[Car class]]; [mapping mapFieldsFromArray:@[@"model", @"year"]]; return mapping; } // Phone mapping + (EKObjectMapping *)objectMapping { EKObjectMapping * mapping = [[EKObjectMapping alloc] initWithObjectClass:[Phone class]]; [mapping mapFieldsFromArray:@[@"number"]]; [mapping mapFieldsFromDictionary:@{ @"ddi" : @"DDI", @"ddd" : @"DDD" }]; return mapping; } // Person mapping + (EKObjectMapping *)objectMapping { EKObjectMapping * mapping = [[EKObjectMapping alloc] initWithObjectClass:[Person class]]; NSDictionary *genders = @{ @"male": @(GenderMale), @"female": @(GenderFemale) }; [mapping mapFieldsFromArray:@[@"name", @"email"]]; [mapping mapKey:@"gender" toField:@"gender" withValueBlock:^(NSString *key, id value) { return genders[value]; }]; [mapping hasOneMapping:[Car objectMapping] forKey:@"car"]; [mapping hasManyMapping:[Phone objectMapping] forKey:@"phones"]; return mapping; } 


Now that the mapping is ready, the creation of the Person object is as follows:

 NSDictionary * personProperties = ...; // JSON  Person,  ,   . Person * person = [[Person alloc] init]; [EKMapper fillObject:person fromExternalRepresentation:personProperties withMapping:mapping]; 


Thus, when creating an object, no if-else constructs are used, there are no checks for NSNull. In addition, existing objects can be easily updated with new data, if such a need arises.

Extra EasyMapping Buns

1. Support CoreData
2. Built-in check for NSNull - NSNull is automatically replaced by nil.
3. The ability to serialize objects in NSDictionary / NSArray
4. Installation support via CocoaPods
5. Fully covered with tests.

Successful practices in implementation

Filling an object using EKMapper is better to bring into the base class, say, BaseModel, as follows:

 - (id)initWithProperties:(NSDictionary *)properties { if (self = [super init]) { [EKMapper fillObject:self fromExternalRepresentation:properties withMapping:[[self class] objectMapping]; } return self; } + (EKObjectMapping *)objectMapping { //Do nothing. Implement in subclass if you want to initialize object //via object mapping return nil; } 


Thus, the creation of the object will look like this:

 Person * person = [[Person alloc] initWithProperties:personProperties]; 


Conclusion


Each quality open-source product is a springboard that allows us as programmers to jump higher and higher. EasyMapping project is only one month, judging by the first commit, however, in my opinion, it can already compete with much older and advanced solutions, at least, with its simplicity. I would like to wish good luck to the author of this interesting framework, and to everyone who finished reading to the end - thanks for your time and good luck traveling JSON!

Links


1. EasyMapping on github
2. CocoaPods

Alternative solutions

1. RestKit
2. Mantle

Source: https://habr.com/ru/post/176721/


All Articles