“Everything has beauty, but not everyone sees it”, Confucius

Introduction

As you might know, JSON is an open standard format that uses human-readable text to transmit data objects consisting of attribute–value pairs. People like this format because it is easy to handle and very straight-forward to understand.

Consequently, it is a very common feature for Mobile Apps to perform connections to servers and fetch remote JSON data. This data must be then parsed and converted into client model objects. This action is something that may happen very often and should be done fast and efficiently. Also, because the server may change the JSON data, adding or removing new fields, our parsing system must be flexible and scalable.

From raw JSON data to data structures

The first step consists in transform JSON data into a usable data structure. This problem is something that has already been solved by Apple using the class NSJSONSerialization. It converts the JSON data into a NSDictionary or NSArray, depending on the JSON.

From JSON data to a Cocoa data structure NSData *data = JSONData; // some JSON data NSError *error = nil; id jsonObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error]; if (error) { NSLog(@"JSON data error: %@", error); return; } // jsonObject is a NSArray or a NSDictionary. // Do whatever you want with your jsonObject 1 2 3 4 5 6 7 8 9 10 11 12 13 NSData *data = JSONData ; // some JSON data NSError *error = nil ; id jsonObject = [ NSJSONSerialization JSONObjectWithData :data options :NSJSONReadingMutableContainers error : & error ] ; if ( error ) { NSLog ( @"JSON data error: %@" , error ) ; return ; } // jsonObject is a NSArray or a NSDictionary. // Do whatever you want with your jsonObject

Perfect, now we have our JSON data inside a usable data structure, but this doesn’t solve our problem. We need to convert JSON data structures to custom model objects and this is a complete different problem.

Model Object Mapping

Here the problem is how to convert (or map) a NSDictionary, representation of JSON data, into a custom object. Multiple approaches can be found in order to solve this problem and we need to find the more simple, scalable and straight-forward one.

1. The Parser Object

We could create a “Parser” object that is responsible of performing the parsing of our objects (probably being a singleton). For example, the class could have methods like -userObjectFromDictionary: that would create a “user object” from the “user JSON data”, and a many other methods, one per each entity to be parsed.

Parser Singleton @interface Parser : NSObject + (Parser*)defaultParser; - (User*)userObjectFromDictionary:(NSDictionary*)dictionary; - (Feed*)feedObjectFromDictionary:(NSDictionary*)dictionary; // etc. @end 1 2 3 4 5 6 7 8 @interface Parser : NSObject + ( Parser * ) defaultParser ; - ( User * ) userObjectFromDictionary : ( NSDictionary * ) dictionary ; - ( Feed * ) feedObjectFromDictionary : ( NSDictionary * ) dictionary ; // etc. @end

However, this first parsing pattern is non-distributed, meaning all parsing code is located in a single place and model-code-sharing needs to be complimented with external parsing methods.

2. Distributed Parsing

Another approach is to do the parsing in a distributed way, having a superclass for our model objects like:

Distributed Parsing Object @interface DistributedParsingObject : NSObject - (id)initWithDictionary:(NSDictionary*)dictionary; - (void)parseFromDictionary:(NSDictionary*)dictionary; @end 1 2 3 4 5 6 @interface DistributedParsingObject : NSObject - ( id ) initWithDictionary : ( NSDictionary * ) dictionary ; - ( void ) parseFromDictionary : ( NSDictionary * ) dictionary ; @end

Both methods “init” and “parse” would map the dictionary keyed-values into their properties and maybe, create other objects and parse them using this recursive pattern.

Issues

However there is a recurrent action that is present in all of them: you must map manually each key-value of the dictionary to your object attribute. Even more, in order to avoid overriding attributes for its key-values that are not in the given dictionary, we must test for each attribute if the key-value is present in the dictionary and if yes, parse that specific key-value for each object attribute.

Parsing a single key-value id value = [dictionary objectForKey:@"SERVER_ATTRIBUTE_NAME"]; if (value != nil) // <-- if we do not test if value is present, we might be overriding a valid object.attribute with a nil value. { // We might want to operate and validate the "value" before assigning it self.OBJECT_ATTRIBUTE_NAME = value; } 1 2 3 4 5 6 id value = [ dictionary objectForKey : @"SERVER_ATTRIBUTE_NAME" ] ; if ( value != nil ) // <-- if we do not test if value is present, we might be overriding a valid object.attribute with a nil value. { // We might want to operate and validate the "value" before assigning it self . OBJECT_ATTRIBUTE_NAME = value ; }

The above lines will have to be coded per each attribute. This is a lot of code. Can you imagine? I’m sure you do.

Also, if we need to support multiple JSON keys for a single attribute or JSON key versioning for different backend versions, the thing becomes even harder: plenty of if statements, lots of casuistic and a code that is becoming hard to maintain and easy breakable.

Therefore, we would like to find a simple, elegant, scalable and automatised solution.

Simplifying with KVC

If we look more in detail, the only think that we must do for sure is define a mapping between the JSON keys and our object properties. Once this is defined, we can feel that everything else can be automatised, and this is what we are going to accomplish with Cocoa’s KVC pattern.

Understanding what is KeyValueCoding (KVC)

We can check in Apple’s documentation of KeyValueCoding:

Key-value coding is a mechanism for accessing an object’s properties indirectly, using strings to identify properties, rather than through invocation of an accessor method or accessing them directly through instance variables

In other words, KVC is a mechanism that allows you to access object properties using strings. What does that mean? easy, you can get and set the value of an object just using a string with its name.

For example, having the following class:

KeyValueCoding sample @interface Video : NSObject @property (nonatomic, strong) NSString *title; @property (nonatomic, strong) NSInteger viewCount; @property (nonatomic, strong) User *uploader; @end 1 2 3 4 5 @interface Video : NSObject @property ( nonatomic , strong ) NSString *title ; @property ( nonatomic , strong ) NSInteger viewCount ; @property ( nonatomic , strong ) User *uploader ; @end

The following code:

KeyValueCoding sample Video *video = [[Video alloc] init]; video.title = @"The awesomeness of being"; video.viewCount = 23; video.uploader = aUserObject; NSLog(@"Title: %@, views: %d, uploader: %@", video.title, video.viewCount, video.uploader.description); 1 2 3 4 5 6 7 Video *video = [ [ Video alloc ] init ] ; video . title = @"The awesomeness of being" ; video . viewCount = 23 ; video . uploader = aUserObject ; NSLog ( @"Title: %@, views: %d, uploader: %@" , video . title , video . viewCount , video . uploader . description ) ;

can be written using Key Value Coding as:

KeyValueCoding sample Video *video = [[Video alloc] init]; 1 Video *video = [ [ Video alloc ] init ] ;

KeyValueCoding sample ; 1 ;

KeyValueCoding sample ; 1 ;

KeyValueCoding sample ; NSLog(@"Title: %@, views: %d, uploader: %@", 1 2 3 ; NSLog ( @"Title: %@, views: %d, uploader: %@" ,

KeyValueCoding sample , [ 1 , [

KeyValueCoding sample intValue], [ 1 intValue ] , [

KeyValueCoding sample description]); 1 description ] ) ;

KeyValueCoding has more functionalities and usages, but with this we have enough for creating model object mapping from a JSON object or a NSDictionary.

Adapting KVC for Model Object Mapping

The main idea is to create a mapping from JSON keys to object attributes names. Then in the object parsing method we can map the JSON key to the object attribute name and perform via KVC the setting of the attribute. We are going to define this into a custom category on NSObject, so magically, all objects in your project will be “parseable” if they implement the mapping from JSON to attribute names.

The interface would be like:

Parsing with KeyValueCoding @interface NSObject (KVCParsing) - (NSDictionary*)mappingForKVCParsing; - (void)parseValue:(id)value forKey:(NSString*)key; - (void)parseValuesForKeysWithDictionary:(NSDictionary*)dictionary; @end 1 2 3 4 5 6 7 8 @interface NSObject ( KVCParsing ) - ( NSDictionary * ) mappingForKVCParsing ; - ( void ) parseValue : ( id ) value forKey : ( NSString * ) key ; - ( void ) parseValuesForKeysWithDictionary : ( NSDictionary * ) dictionary ; @end

The first method, -mappingForKVCParsing, should return a dictionary with the mapping from the JSON keys to the object property names. The second method sets a value for a specific key. This key is going to be mapped using the dictionary returned by -mappingForKVCParsing. Finally, the last method will set the key-values contained in the dictionary using the same pattern as described in the second method.

Here we can find the implementation of these methods:

Parsing with KeyValueCoding @implementation NSObject (KVCParsing) - (NSDictionary*)mappingForKVCParsing { // By default, return empty dictionary. Subclasses of NSObject may return custom mappings. // The dictionary should contain the mapping from JSON keys to object property names. // Example: @{@"json_attribute_name": @"object_property_name", ... }; return @{}; } - (void)parseValue:(id)value forKey:(NSString*)key { // First map the given key NSString *mappedKey = [[self mappingForKVCParsing] valueForKey:key]; // If mapped key is found, use the mapped key instead of the given key if (mappedKey) key = mappedKey; // Perform validation over the given value. For more formation about KVC validation check references below. NSError *error = nil; BOOL validated = [self validateValue:&value forKey:mappedKey error:&error]; if (validated) // if validated, proceed to set the value { if (value != [NSNull null] && value != nil) // if value is not nil and not NSNull, assign it via KVC [self setValue:value forKey:mappedKey]; else // Otherwise, if nil or NSNull, assign nil value via KVC [self setNilValueForKey:mappedKey]; } else // otherwise, abort the value setter { NSLog(Value for Key %@ is not valid in class %@. Error: %@", key, [self.class description], error); } } - (void)parseValuesForKeysWithDictionary:(NSDictionary *)keyedValues { for (NSString *key in keyedValues) // iterate over all keys in dictionary { id value = keyedValues[key]; [self parseValue:value forKey:key]; // parse each individual key-value } } @end 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 @implementation NSObject ( KVCParsing ) - ( NSDictionary * ) mappingForKVCParsing { // By default, return empty dictionary. Subclasses of NSObject may return custom mappings. // The dictionary should contain the mapping from JSON keys to object property names. // Example: @{@"json_attribute_name": @"object_property_name", ... }; return @ { } ; } - ( void ) parseValue : ( id ) value forKey : ( NSString * ) key { // First map the given key NSString *mappedKey = [ [ self mappingForKVCParsing ] valueForKey :key ] ; // If mapped key is found, use the mapped key instead of the given key if ( mappedKey ) key = mappedKey ; // Perform validation over the given value. For more formation about KVC validation check references below. NSError *error = nil ; BOOL validated = [ self validateValue : & value forKey :mappedKey error : & error ] ; if ( validated ) // if validated, proceed to set the value { if ( value != [ NSNull null ] && value != nil ) // if value is not nil and not NSNull, assign it via KVC [ self setValue :value forKey :mappedKey ] ; else // Otherwise, if nil or NSNull, assign nil value via KVC [ self setNilValueForKey :mappedKey ] ; } else // otherwise, abort the value setter { NSLog ( Value for Key % @ is not valid in class % @ . Error : % @ " , key , [ self . class description ] , error ) ; } } - ( void ) parseValuesForKeysWithDictionary : ( NSDictionary * ) keyedValues { for ( NSString *key in keyedValues ) // iterate over all keys in dictionary { id value = keyedValues [ key ] ; [ self parseValue :value forKey :key ] ; // parse each individual key-value } } @end

Remark that we are calling the KVC validation method before setting the value via KVC. This will allow the developer to check if the value type is correct or if it should be converted (or parsed) into another type of object before the setting is done.

To understand how this system will work, lets give an example.

Sample parsing via KVC

Let’s reuse our video class listed above, that should be parsed from the following JSON:

KVC Parsing example {"video_title":"The best video ever", "video_views_total": 767, "video_author": {"user_name": "johndoe", "age": 17, "total_video_count": 23, } } 1 2 3 4 5 6 7 { "video_title" : "The best video ever" , "video_views_total" : 767 , "video_author" : { "user_name" : "johndoe" , "age" : 17 , "total_video_count" : 23 , } }

In order to use the KVCParsing category we need to override the method -mappingForKVCParsing and validate those fields that doesn’t fit the type of the object:

KVC Parsing example @implementation Video // Overriding method for return the custom mapping. - (NSDictionary*)mappingForKVCParsing { // No need to call [super mappingForKVCParsing] because it is empty return @{@"video_title": @"title", @"video_views_total": @"viewCount", @"video_author": @"uploader", }; } // KVC Validation. For more information check references below. - (BOOL)validateUploader:(id *)ioValue error:(NSError * __autoreleasing *)outError { // If *ioValue is a NSDictionary, the JSON container, we need to parse it into a "User" object if ([*ioValue isKindOfClass:[NSDictionary class]]) { // Create an empty recipient of type "User" User *user = [[User alloc] init]; // Assign the key-values of the dictionary using the parsing method [user parseValuesForKeysWithDictionary:*ioValue]; // Finally, reassign the new value and drop the dictionary *ioValue = user; } // Return YES because the *ioValue now is valid return YES; } @end 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 @implementation Video // Overriding method for return the custom mapping. - ( NSDictionary * ) mappingForKVCParsing { // No need to call [super mappingForKVCParsing] because it is empty return @ { @"video_title" : @"title" , @"video_views_total" : @"viewCount" , @"video_author" : @"uploader" , } ; } // KVC Validation. For more information check references below. - ( BOOL ) validateUploader : ( id * ) ioValue error : ( NSError * __autoreleasing * ) outError { // If *ioValue is a NSDictionary, the JSON container, we need to parse it into a "User" object if ( [ *ioValue isKindOfClass : [ NSDictionary class ] ] ) { // Create an empty recipient of type "User" User *user = [ [ User alloc ] init ] ; // Assign the key-values of the dictionary using the parsing method [ user parseValuesForKeysWithDictionary : *ioValue ] ; // Finally, reassign the new value and drop the dictionary *ioValue = user ; } // Return YES because the *ioValue now is valid return YES ; } @end

Therefore, now you can parse your Video objects (and your User objects) by simply doing:

KVC Parsing example NSData *jsonData = SOME_JSON_DATA; NSError *error = nil; id jsonObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error]; if (!error) { Video *newVideo = [[Video alloc] init]; 1 2 3 4 5 6 7 8 NSData *jsonData = SOME_JSON_DATA ; NSError *error = nil ; id jsonObject = [ NSJSONSerialization JSONObjectWithData :data options :NSJSONReadingMutableContainers error : & error ] ; if ( ! error ) { Video *newVideo = [ [ Video alloc ] init ] ;

KVC Parsing example } 1 }

Benefits of using KVCParsing

Distributed parsing pattern

Each object is responsible of its own parsing. Using the KVCParsing category on NSObject this is even more evident, thus now all NSObject may be parseable.

Minimising code

The only code you must implement for parsing an object is the server-object mapping and the specific cases of data value validation. The rest is automatic. We have reduced the amount of code you have to do to the minimum.

Only interacting with the received key-values

Because KVC will only set those key-values you are setting, you won’t be interacting with the others available keys or properties of your objects. No need to test if your dictionary contains or not a key-value in order to avoid overriding it with a nil value.

Non-injective mappings

You can define non-injective mappings to your object properties. This means you can establish multiple JSON names for a single property. This is very useful if your backend service is not consistent with names.

Logging undefined keys

If you try to set a property via KVC with an undefined key, the system by default will throw an exception and stop your application. However, you can override the KVC method -setValue:forUndefinedKey: and you will be able to handle those values for undefined keys. You can log them and know when the server is sending unknown key-values or anything else you want.

Simple, Powerful & Scalable

We have designed a very simple but powerful system. You can easily add new mappings and even define your object mappings on execution time (being able to chose between multiple versions of your backend system).

Check it out!

You will find Motis category on NSObject for KVC object mapping in the GitHub repository of Mobile Jazz. Also, you can include it directly using CocoaPods in your projects.

GitHub: https://github.com/mobilejazz/Motis

CocoaPods: pod 'Motis', '~> 0.1.0'

References

78EBA888-1918-4C8C-B455-3F3BBC7BD051 Created with sketchtool. Liked it?