I've been searching if it is possible to get a JSON dictionary or array and directly map it in a custom object whose properties have the same name as the JSON tags, but I din't find any information regarding that.
I've been parsing JSON dictionaries manually, like this:
id deserializedObj = nil;
id jsonObject = [NSJSONSerialization JSONObjectWithData:jsonData
options:NSJSONReadingAllowFragments
error:&error];
if ([jsonObject isKindOfClass:[NSDictionary class]]) {
NSDictionary *jsonDictionary = (NSDictionary *)jsonObject;
if ([jsonDictionary objectForKey:idTag] != [NSNull null])
[myObject setID:[[jsonDictionary objectForKey:@"id"] integerValue]];
// Rest of properties
}
But I find weird having to parse each dictionary entry manually and not having a way to directly serialize it into a custom object, isn't there any other and faster way?
Note: I need my app to be compatible with iOS 5+
Thanks in advance
I would propose you a library called Motis. It is a category on NSObject that does object mapping via KeyValueCoding. The nice thing of this category is that is very light-weight and performs automatic value validation trying to fit the class type of your properties (via introspection).
Most of all, what you have to do is define mappings (dictionaries) in your NSObject subclasses with pairs of "JSONKey": "PropertyName".
Also, if you have arrays, you can define mappings from your array names to the class types of its content enabling automatic object mapping for array content.
For example, If we try to map to our custom objects the following JSON:
{"video_id": 23,
"video_title": "My holidays in Paris",
"video_uploader":{"user_id":55,
"user_username": "johndoe"
},
"video_people":[{"user_id":55,
"user_username": "johndoe"
},
{"user_id":45,
"user_username": "jimmy"
},
{"user_id":55,
"user_username": "martha"
}
]
}
we would implement in our NSObject subclasses:
// --- Video Object --- //
@interface Video : NSObject
@property (nonatomic, assing) NSInteger videoId;
@property (nonatomic, strong) NSString *title;
@property (nonatomic, strong) User *uploader;
@property (nonatomic, strong) NSArray *peopleInVideo; // <-- Array of users
@end
@implementation Video
+ (NSDictionary*)mts_mapping
{
return @{@"video_id" : mts_key(videoId),
@"video_title" : mts_key(title),
@"video_uploader" : mtsk_key(uploader),
@"video_people": mts_key(peopleInVideo),
};
}
+ (NSDictionary*)mts_arrayClassMapping
{
return @{mts_key(peopleInVideo): User.class};
}
@end
// --- User Object --- //
@interface User : NSObject
@property (nonatomic, assing) NSInteger userId;
@property (nonatomic, strong) NSString *username;
@end
@implementation User
+ (NSDictionary*)mts_mapping
{
return @{@"user_id" : mts_key(userId),
@"user_username" : mts_key(username),
};
}
@end
And to perform the parsing and object mapping:
NSData *data = ... // <-- The JSON data
NSError *error = nil;
NSDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
if (error)
return; // if error, abort.
Video *video = [[Video alloc] init];
[video mts_setValuesForKeysInDictionary:jsonObject]; // <-- Motis Object Mapping
NSLog(@"Video: %@", video.description);
That simple. Here you can read more about it: http://github.com/mobilejazz/Motis
For more information check the blog post on benefits of using KeyValueCoding and distributed object mapping I've wrote in MobileJazz blog.
Hoping to be helpful.
You can try using JTObjectMapping inspired from RestKit.
The other way is to first remove null and nil values from dictionary. Create a mapping for your keys to keypath in your subclass using setValue:forKeyPath:
You can create your own NSObject subclasses with their properties and populate them using Key-Value Coding in loop where you explore all NSDictionary keys returned from JSON deserialization. Of course each dictionary key should match an object property.
Key value coding