JSONModel的使用详解
JSONModel is a data model framework for iOS and OSX. It‘s written in Objective-C and helps you in several different ways.You can read more about its key features below:
- Rapidly write model code
- Validation of the model‘s input
- Atomic data
- Type casting between JSON and Obj-C
- Built-in data transformers
- Custom data transformers
- Model cascading
- Convert back & forth from/to JSON
- Persist model state in memory or file
- Create models straight from the Internet
- Automatic model compare methods
self.id = [jsonDict objectForKey:@"id"];
self.name = [jsonDict objectForKey:@"name"];
self.profileImageBig = [jsonDict objectForKey:@"profile_image_big"];
self.profileImageSmall = [jsonDict objectForKey:@"profile_image_small"];
self.profileImageSquare = [jsonDict objectForKey:@"profile_image_square"];
self.firstName = [jsonDict objectForKey:@"firstName"];
self.familyName = [jsonDict objectForKey:@"familyName"];
self.age = [jsonDict objectForKey:@"age"];
What sucks about this code is that you first need to declare all you properties in your interface file and then go ahead and write init method for your model that just reads the JSON through and copy all the values over to your propertis.That‘s a lot of manual labor! But hey - it‘s the future now!
JSONModel removes the need to write initializer code to your models.
Just declare properties in your model with the same names as the keys in your JSON and voila! The heavy work is done automatically for you.
So, just declare your properties like usual in your interface file:
@interface MyModel: JSONModel
@property (strong, nonatomic) NSString* id;
@property (strong, nonatomic) NSString* name;
(etc...)
@end
JSONModel‘s inherited initWithDictionary: method will take care to match incoming JSON keys with your model‘s properties, and copy over the matching ones. No need of any code in your implementation file.self.profileURL = [jsonDict objectForKey:@"profileURL"]; //TODO: what to do if there‘s no url in the JSON? should we handle this somehow? if (!self.profileURL) NSLog(@"something‘s broken with the json API");When you write a model the traditional way, you also need to validate whether the keys your model need are found in the incoming JSON.
However, that‘s not always what‘s happening (partially due to the traditional way of handling errors in Cocoa, I wouldn‘t blame anybody for skipping on that).
Problem is - in the end you either write all the code to check whether the JSON keys exist, or you leave your app to free fall when there‘s a problem.
If your models inherit JSONModel, there‘s no need for extra code to check whether the required JSON structure is in place. JSONModel‘s initWithDictionary: method checks that automatically.
All properties you define in your model will be required. If they are not present in the input, you‘ll get an exception.
However you can also define some of the properties as not-required, and that‘s also very easy:
//this property is required @property (strong, nonatomic) NSString* string; //this one‘s optional @property (strong, nonatomic) NSNumber<Optional>* number;
if (jsonDict[@"name"])
labelName.text = jsonDict[@"name"];
else
[self showErrorMessageAndBailout];
if (jsonDict[@"age"])
labelAge.text = jsonDict[@"age"];
else
[self showErrorMessageAndBailout];
Do you notice the problem? The code reads and validates and data, AND in the same time updates the user interface. So, when an error occurs there‘s no way to rollback the already applied changes.
JSONModel does all data reading and validation atomically. You call one method, which either succeeds or fails and then you can use the data being sure it‘s valid and alright.
Here‘s how you make an instance of your data model when it contains just few strings and numbers:
SimpleModel* model = [[SimpleModel alloc] initWithString:@"...json here..." error:nil];
And here‘s how you make an instance of model, which contains other models, and has JSON data converted to custom classes when read:
SuperComplicatedModel* model = [[SuperComplicatedModel alloc] initWithString:@"...json here..." error:nil];
And here is how you read a list of 100 models, as each of them is the described above model:
NSArray* models = [SuperComplicatedModel arrayOfObjectsFromDictionaries: jsonDatas error:nil];
As you see - instantiation is always one-liner, which you can comfortably error handle.If you catch my drift - the JSON format is an incredible data type bottleneck in between your app and your server back end. While JSONModel lives on iOS it cannot do much about the part on the right side of JSON - the communication to your server.
What it can do is to help you cast the JSON data to Objective-C types!
Just define your properties‘ types as the types you‘d like to have and JSONModel will try to convert the incoming data to satisfy your needs.
Have a look at this JSON feed:
{
"first" : 1,
"second": 35,
"third" : 10034,
"fourth": 10000
}
You have four keys and their values are numbers, therefore you get the same old NSNumber back when you parse the JSON yourself.Not with JSONModel you don‘t! You just specify the types of your properties and JSONModel then converts the data for you, like so:
@interface NumbersModel: JSONModel
@property (assign, nonatomic) short first;
@property (assign, nonatomic) double second;
@property (strong, nonatomic) NSNumber* third;
@property (strong, nonatomic) NSString* fourth;
@end
Again - no implementation code needed for this to work!The built-in transformers work behind the scene, so you‘d never even have to know they kick in and do the heavy lifting for you.
For example you might have this JSON:
{
"purchaseDate" : "2012-11-26T10:00:01+02:00",
"blogURL" : "http://www.touch-code-magazine.com"
}
This JSON includes a date in W3C format (widely used in JSON APIs), but it comes to your iOS app as a string, because JSON does not have a data type "date".The second element in the JSON code is a URL; again it comes to you as plain string, since JSON does not have a URL data type.
To make use of the built-in transformers, just create your model as:
@interface SmartModel: JSONModel
@property (strong, nonatomic) NSDate* purchaseDate;
@property (strong, nonatomic) NSURL* blogUrl;
@end
JSONModel see that there‘s a mismatch between the incoming types and your model‘s properties types, and that when the transformers kick in and convert the values. In the end you just use your properties as normal. Smooth.You can add transformers to JSONValueTransformer for your own project.
You need to make a new category on JSONValueTransformer and add two methods to this category. That‘s all. Like so:
@interface JSONValueTransformer(UIColor)
-(UIColor*)UIColorFromNSString:(NSString*)string;
-(id)JSONObjectFromUIColor:(UIColor*)color;
@end
This example adds transformer for a UIColor* property, which gets its value from a HEX encoded color coming in as a string. The first method you declare is named like so:-(YourPropertyClass*)YourPropertyClassFromJSONObjectClass:(JSONObjectClass*)name;
Therefore since the HEX color comes from JSON as an NSString, and your property is of type UIColor* your method name should be:
-(UIColor*)UIColorFromNSString:(NSString*)string;
It makes sense? It‘s easy? Yes - I agree.The second method you need for exporting your model to JSON (if you need to do that). It works in the same way, only that it doesn‘t imply what is the exported JSON object data type. It‘s up to you to decide.
Check the included JSONValueTransformer+UIColor.h/m files for a full example how to write custom transformers.
Let me show you an example of an image, which beside simple properties, also contains a copyright, which is a model itself.
Here‘s the JSON you get:
{
"idImage": 1,
"name": "house.jpg",
"copyright": {"author":"Marin Todorov", "year":2012}
}
It‘s pretty simple to define your models for this JSON. Just have first your copyright model defined:
@interface CopyModel: JSONModel
@property (strong, nonatomic) NSString* author;
@property (assign, nonatomic) int year;
@end
And then define your Image model, which contains the copyright model, like so:
#import "CopyModel.h"
@interface ImageModel: JSONModel
@property (assign, nonatomic) int idImage;
@property (strong, nonatomic) NSString* name;
@property (strong, nonatomic) CopyModel* copyright;
@end
That‘s it. When you initialize an instance of the ImageModel, it will see that one of the properties is a JSONModel itself, and will parse properly the JSON input. Boom!How about when you don‘t have a single model as a property, but a list of models for example?
Well JSON lists come through as NSArray objects, you just have to also specify which model you expect in the list:
@property (strong, nonatomic) NSArray<TweetModel>* tweets;
And for that to work you also need to add a protocol definition to your model interface file, and the protocol name should match your model name. So for a model called TweetModel (as in the example above), add in TweetModel.h:
@protocol TweetModel @end
That‘s it. Now your top model can cascade inside the JSON structure and read through multilevel structures.You can then easily turn your model to a JSON compliant object at any time by calling toDictionary on your model‘s instance.
Or you can just get the text representation of your current model data by calling toJSONString.
Pretty handy if you need to send the modified model back over the network to your server!
Loading model form the disc:
//load from file NSDictionary* object = [NSDictionary dictionaryWithContentsOfFile:filePath]; //initialize model with data data = [[MyDataModel alloc] initWithDictionary: object];And saving it back to the disc:
//save to disc [[data toDictionary] writeToFile:filePath atomically:YES];
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。