Small library that parse almost automatically JSON and map it to any object, works with NSManagedObject.
My object
@interface User : NSObject
@property (nonatomic, strong) NSNumber *userID;
@property (nonatomic, strong) NSString *firstName;
@property (nonatomic, strong) NSDate *createdAt;
@end
My json
{
"id": 14,
"first_name": "Wernimont",
"created_at": "2013-12-12T14:11:10Z"
}
User *user = [[BWObjectMapper shared] objectFromJSON:JSON withObjectClass:[User class]];
That's it ! JSON will be magically serialized as a user object.
@interface User : NSObject
@property (nonatomic, strong) NSNumber *userID;
@property (nonatomic, strong) NSString *firstName;
@property (nonatomic, strong) NSDate *createdAt;
@end
[BWObjectMapping mappingForObject:[User class] block:^(BWObjectMapping *mapping) {
[mapping mapPrimaryKeyAttribute:@"id" toAttribute:@"userID"];
[mapping mapKeyPath:@"first_name" toAttribute:@"firstName"];
[mapping mapAttributeFromArray:@[@"name"]];
[mapping mapAttributeFromDictionary:@{@"created_at" : @"createdAt"}];
[[BWObjectMapper shared] registerMapping:mapping withRootKeyPath:@"user"];
}];
At the last line we register the mapping and give a root key path. You don't need to have one, but if not the mapper will not be able to guess which mapping class to use.
By default if you follow the following naming convention you don't need to manually set the mapping
json key -> Model attribute name
name -> name
id -> postID
user_name -> userName
Above example become
[BWObjectMapping mappingForObject:[User class] block:^(BWObjectMapping *mapping) {
[mapping mapPrimaryKeyAttribute:@"id" toAttribute:@"userID"];
[mapping mapKeyPath:@"first_name" toAttribute:@"firstName"];
[mapping mapAttributeFromArray:@[@"name"]];
[mapping mapAttributeFromDictionary:@{@"created_at" : @"createdAt"}];
[[BWObjectMapper shared] registerMapping:mapping withRootKeyPath:@"user"];
}];
Or even shorter
[[BWObjectMapper shared] registerMappingForClass:[User class] withRootKeyPath:@"user"];
[[BWObjectMapper shared] objectWithBlock:^id(Class objectClass, NSString *primaryKey, id primaryKeyValue, id JSON, id userInfo) {
return [[objectClass alloc] init];
}];
{
"user": [{
"id": 1,
"first_name": "Bruno",
"created_at": "2012-08-10T06:12:28Z"
}]
}
Default parsing date format is Rails format.
NSArray *objects = [[BWObjectMapper shared] objectsFromJSON:JSON];
Because the JSON contain a root key path, the mapping automatically discover.
- (NSArray *)objectsFromJSON:(id)JSON withMapping:(BWObjectMapping *)mapping;
- (NSArray *)objectsFromJSON:(id)JSON withObjectClass:(Class)objectClass;
- (NSArray *)objectsFromJSON:(id)JSON;
- (id)objectFromJSON:(id)JSON withMapping:(BWObjectMapping *)mapping;
- (id)objectFromJSON:(id)JSON withMapping:(BWObjectMapping *)mapping existingObject:(id)object;
- (id)objectFromJSON:(id)JSON withObjectClass:(Class)objectClass;
- (id)objectFromJSON:(id)JSON withObjectClass:(Class)objectClass existingObject:(id)object;
- (id)objectFromJSON:(id)JSON;
- (id)objectFromJSON:(id)JSON existingObject:(id)object;
If you don't use Rails date format you have two options:
-
Specify global date format
[objectMapping mapKeyPath:@"created_at" toAttribute:@"createdAt" dateFormat:@""];
-
Custom date format on each attribute.
[[BWObjectMapper shared] setDefaultDateFormat:@""];
Let's suppose you have a JSON like this:
{
"model": "HB20",
"year": "2013",
"engine": {
"type": "v8"
},
"wheels": [
{
"id": "123123123",
"type": "16"
},
{
"id": "1234",
"type": "17"
}
]
}
- First define your models:
@interface Car : NSObject
@property (nonatomic, copy) NSString *model;
@property (nonatomic, copy) NSString *year;
@property (nonatomic, strong) Engine *engine;
@property (nonatomic, strong) NSArray *wheels;
@end
@interface Engine : NSObject
@property (nonatomic, copy) NSString *type;
@end
@interface Wheel : NSObject
@property (nonatomic, copy) NSString *identifier;
@property (nonatomic, copy) NSString *size;
@end
- After this what you need to do is define their mappings in somewhere like this:
#import "MappingProvider.h"
#import "Car.h"
#import "Engine.h"
#import "Wheel.h"
@implementation MappingProvider
+ (BWObjectMapping *)carMapping
{
return [BWObjectMapping mappingForObject:[Car class] block:^(BWObjectMapping *mapping) {
[mapping mapAttributeFromArray:@[@"model", @"year"]];
[mapping hasOneWithRelationMapping:[self engineMapping] fromKeyPath:@"engine"];
[mapping hasManyWithRelationMapping:[self wheelMapping] fromKeyPath:@"wheels"];
}];
}
+ (BWObjectMapping *)engineMapping
{
return [BWObjectMapping mappingForObject:[Engine class] block:^(BWObjectMapping *mapping) {
[mapping mapAttributeFromArray:@[@"type"]];
}];
}
+ (BWObjectMapping *)wheelMapping
{
return [BWObjectMapping mappingForObject:[Wheel class] block:^(BWObjectMapping *mapping) {
[mapping mapAttributeFromArray:@[@"size"]];
[mapping mapAttributeFromDictionary:@{
@"id": @"identifier"
}];
}];
}
- And to instanciate the root object you can do this:
Car *car = [[BWObjectMapper shared] objectFromJSON:carJSON withMapping:[MappingProvider carMapping]];
Copy BWObjectMapper dir into your project.
pod 'BWObjectMapping', :git => "https://github.com/brunow/BWObjectMapping.git", :tag => "0.5.1"
github "brunow/BWObjectMapping" ~> 0.5.1
BWObjectMapper is ARC only.
Big thanks to lucasmedeirosleite that added hasMany and hasOne relation.
Bruno Wernimont
- Twitter - @brunowernimont