Skip to content
kechan edited this page Aug 2, 2013 · 33 revisions

You use KEJSONModel by subclassing your object model from the KEJSONModel superclass. A step by step example is provided here (this is also included in the Xcode project's unit test). As you walk through the steps, try to consider how it should be done for your own specific JSON message.

###Step by Step

(0) We start with this JSON message:

{
    "menuItems" : [{
                   "id": "JAP122",
                   "name": "Teriyaki Bento",
                   "spicy_level": "2",
                   "description" : "Teriyaki Bento is one of the best",
                   "review-count" : "4",
                   "reviews" : [{
                                "id" : "rev1",
                                "reviewText" : "This is an awesome place to eat",
                                "reviewerName" : "Awesome Man",
                                "rating" : "5"
                    }]
                   }],
    "status" : {
        "code": "0",
        "localdesc": "Everything is alright."
    },
}

As KEJSONModel is a young and personal project, it may not handle every situation. So if your JSON's structure looks similar to the above message, then it will likely work.

Note that the top level is a hash with 2 keys "menuItems" and "status" whose corresponding values are of Array and Hash type.

(1) Starting from the "leaves" and moving up, identify any keys whose values are not simple string or number. For our exmaple, you can identify "status", "reviews", and then finally "menuItems".

(2) For those keys whose values are hashes, we create a corresponding subclass of KEJSONModel. eg. For status,

@interface Status : KEJSONModel
@property (nonatomic, strong) NSString *code;
@property (nonatomic, strong) NSString *localdesc;
@end

@implementation Status
@end

You declare its properties to match its hash, whose keys are "code" and "localdesc". Whenever possible, try to use the same name (case-sensitive) as the JSON's, except for objective-c classes where they are capitalized.

(3) For the other keys whose values are array of objects, we want to create a subclass that correspond to the individual element. You MUST name this subclass by dropping the trailing 's' (making it singular). ie. for "reviews", we are going to create a class named "Review". This convention is strongly assumed. If your JSON keys are not plural (but whose values are arrays), there's a way around it by remapping the names (this is discussed in step 4, please read on).

@interface Review : KEJSONModel
@property (nonatomic, strong) NSString *reviewId;
@property (nonatomic, strong) NSString *reviewText;
@property (nonatomic, strong) NSString *reviewerName;
@property (nonatomic, strong) NSString *rating;
@end

@implementation Review
// this is not empty, please see Step (4)
@end

(4) Remap/rename things like "id", "description" in your properties. You may notice that in the JSON message, review has a key called "id". It is not wise to use this name for your property because id is reserved in objective-c. Another well known example is "description". These are properties that are already defined in the super-class NSObject. Thus, we want to re-map the "id" key to be "reviewId" property in the class. This is taken care of in the implementation, like so:

@implementation Review
+(NSDictionary *) jsonKeyToObjectPropertyNameMap {
    return @{@"id": @"reviewId"};
}
@end

Override the class method +(NSDictionary *)jsonKeyToObjectPropertyNameMap by returning an NSDictionary containing the renaming map.

(5) We are still left with "menuItems", following the same advice from Step (3), we create a class called "MenuItem". Since we have already defined the class Review, we can now declare a property of NSMutableArray of *reviews (and yes, using the same key names as the JSON as much as possible):

@interface MenuItem : KEJSONModel
@property (nonatomic, strong) NSString *itemId;
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *spicyLevel;
@property (nonatomic, strong) NSString *itemDescription;
@property (nonatomic, strong) NSString *reviewCount;
@property (nonatomic, strong) NSMutableArray *reviews;  // this is going to hold an array of Review
@end

@implementation MenuItem
+(NSDictionary *) jsonKeyToObjectPropertyNameMap {
    return @{@"id": @"itemId", @"description": @"itemDescription"};
}
@end

Notice that some of the JSON keys has underscores and dashes. I found this quite a frequent practice from certain REST API's JSON response, so we automatically match it up with the camel case version in the properties. For "spicy_level", it is automatically spicyLevel and "review-count" is "reviewCount". Similar to step (4), we also provide the NSDictionary to rename json keys to property mapping in the implementation to avoid conflicts.

(6) Putting it all together. We create a class called "Menu", to hold it all together. We have all the "ingredient" classes we need: Status, MenuItem, and Review.

@interface Menu : KEJSONModel
@property (nonatomic, strong) NSMutableArray *menuItems;
@property (nonatomic, strong) Status *status;
@end

@implementation Menu
@end

You can compare this with the root level of the JSON message, and see how it now makes sense to write the above.

(7) Instantiation. With all the effort you have spent, it is time to reap the reward. Let's use it our application code. Assume you have obtained your JSON message as an NSData object, just do the following:

NSMutableDictionary *dict = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
Menu *menu = [[Menu alloc] initWithDictionary:dict];

NSLog(@"Our menu has %d items", menu.menuItems.count);
NSLog(@"First menu item is %@", menu.menuItems[0].name);

Full sample source code

Please look under the KEJSONModelTests folder in the project.

Current limitation and work in progress

These are tracked and discussed under Issues. It also doesn't handle NSCoding, NSCopying or NSMutableCoping protocols and related implementations. These are minimal objects. If you need those protocols, you can roll your own implementations (they are pretty standard, just read some Apple doc). However, if I receive enough requests, they could be added (using some sensible defaults).

Clone this wiki locally