轉發自 http://blog.csdn.net/smking/article/details/40432287json
JSONModel, Mantleapi
這兩個開源庫都是用來進行封裝JSON->Model的, 想一想看, 直接向服務器發起一個請求,而後回來後,就是一個Model, 直接使用, 這是一個多麼美好的事情。 感謝GitHub的開源精神。數組
那咱們開始吧。 服務器
先說說這兩個的差異。網絡
這兩個使用的方法其實都差很少, 詳細的使用方法請直接GitHub上找, 仍是比較簡單地。 就我我的來講JSONModel相對起來使用較爲簡單,而Mantle使用起來略爲複雜,可是Mantle彷佛比JSONModel更爲強大。 app
通常的項目,其實使用JSONModel就已經足夠。 dom
下面講一下JSONModel的使用方法。 oop
@inteface MyModel : JSONModelpost
1. 使用JSONModel時,不須要額外去檢查所要的服務器屬性是否有返回。JSONModel的initWithDictionary方法會自動去進行檢查並處理。性能
2. 有效性檢查,若是指定的服務器返回的某個字段沒有返回,並且又是必須的, 像下面這樣寫,則會拋出異常。
//this property is required
@property (strong, nonatomic) NSString* string;
由於默認這個值是必須的。
通常狀況下,咱們不想由於服務器的某個值沒有返回就使程序崩潰, 咱們會加關鍵字Optional.
//this one's optional
@property (strong, nonatomic) NSNumber<Optional>* number;
3. 原子數據, 以前多是以下面這樣操做數據
if (jsonDict[@"name"])
labelName.text = jsonDict[@"name"];
else
[self showErrorMessageAndBailout];
這段代碼會使得jsonDict[@"name"], 會被讀取,而後進行有效性判斷,最後再被使用。 換句話來講,這裏使用了三次, 而若是某些狀況下,使用一次就已經出錯,但卻沒法阻止它接下來的連續出錯。
而若是使用JSONModel的屬性,則只會保證上面只使用一次,就能夠進行有效性的判斷以及使用。(其實上面也能夠作到,只須要把這個值取出來,存下來接着使用卻可,可是代碼會稍顯麻煩)
同時讀取一批數據以下面代碼:
簡單模型以下:
SimpleModel* model = [[SimpleModel alloc] initWithString:@"...json here..." error:nil];
複雜模型以下, 這裏假設複雜模型包含了簡單模型。主要是爲了說明模型以前的包含狀況下,照樣能夠進行解析。
SuperComplicatedModel* model = [[SuperComplicatedModel alloc] initWithString:@"...json here..." error:nil];
模型的批處理,即一次能夠處理一批模型。
NSArray* models = [SuperComplicatedModel arrayOfObjectsFromDictionaries: jsonDatas error:nil];
4. 數據轉換, OC <-> JSON
注意下面這張圖:這意味着JSON的數據格式只有中間的部分, string,number, array, object, 以及null
例若有以下 JSON數據:
{
"first" : 1,
"second": 35,
"third" : 10034,
"fourth": 10000
}
能夠以下定義這個模型
@interface NumbersModel:JSONModel
@property (assign,nonatomic) short first;
@property (assign,nonatomic) double second;
@property (strong,nonatomic) NSNumber* third;
@property (strong,nonatomic) NSString* fourth;
@end
注:JSON數據中, first爲1,second爲35, 可是它們卻能夠自動被轉換成short, double類型。 對於10034, 以及10000會自動轉換爲NSNumber以及NSString。 這些都是JSONModel會自動進行的。 神奇吧!
5. 內嵌的數據轉換, 在JSONValueTransformer類中,有各類內嵌的轉換支持。以下面
{
"purchaseDate" : "2012-11-26T10:00:01+02:00",
"blogURL" : "http://www.touch-code-magazine.com"
}
分別是一個 日期類型,以及一個URL類型。
@interface SmartModel: JSONModel
@property (strong, nonatomic) NSDate* purchaseDate;
@property (strong, nonatomic) NSURL* blogUrl;
@end
用上面這個模型,不須要其它代碼,便可以獲得想要的轉換 。
這個JSONValueTransformer類中有以下支持的轉換
NSMutableString <-> NSString
NSMutableArray <-> NSArray
NS(Mutable)Array <- JSONModelArray
NSMutableDictionary <-> NSDictionary
NSSet <-> NSArray
BOOL <-> number/string
string <-> number
string <-> url
string <-> time zone
string <-> date
number <-> date
6. 自定義數據轉換, 顯然上面的內嵌轉換有時不能知足咱們的須要,因此咱們須要以下的自定義轉換。
你須要作的就是自定義一個JSONValueTransformer的類別文件,以下:
@interface JSONValueTransformer(UIColor)
-(UIColor*)UIColorFromNSString:(NSString*)string;
-(id)JSONObjectFromUIColor:(UIColor*)color;
@end
而後再進行實現便可。
注意上面的命名是採用:
-(YourPropertyClass*)YourPropertyClassFromJSONObjectClass:(JSONObjectClass*)name;
例如:
-(UIColor*)UIColorFromNSString:(NSString*)string;
而要把這個類型轉換爲JSON,則像這樣便可:(注下面這個id,能夠修改也能夠不用修改爲NSString,由於必定知道這是一個nsstirng. heqin:這裏其實也可能會是其它類的,應該是根據特定狀況來特定判斷。)
-(id)JSONObjectFromYourPropertyClass:(YourPropertyClass*)color;
以下是另外一個例子:
@implementation JSONValueTransformer (CustomTransformer)
- (NSDate *)NSDateFromNSString:(NSString*)string {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:APIDateFormat];
return [formatter dateFromString:string];
}
- (NSString *)JSONObjectFromNSDate:(NSDate *)date {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:APIDateFormat];
return [formatter stringFromDate:date];
}
@end
7. 層級嵌套。
{
"idImage": 1,
"name": "house.jpg",
"copyright": {"author":"Marin Todorov", "year":2012}
}
@interface CopyModel: JSONModel
@property (strong, nonatomic) NSString* author;
@property (assign, nonatomic) int year;
@end
#import "CopyModel.h"
@interface ImageModel: JSONModel
@property (assign, nonatomic) int idImage;
@property (strong, nonatomic) NSString* name;
@property (strong, nonatomic) CopyModel* copyright;
@end
而後在你獲得的ImageModel後, 就會發現其中的CopyModel也有數據了。
若是你想要一個數組的模型屬性,以下便可。
@property (strong, nonatomic) NSArray<TweetModel>* tweets;
8. JSONModel轉換爲Dictioanry, JSONString.
直接使用JSONModel的方法toDictioanry, 以及toJSONString便可。
9. 保存model數據
NSDictionary* object = [NSDictionary dictionaryWithContentsOfFile:filePath];
data = [[MyDataModel alloc] initWithDictionary: object];
//保存操做
[[data toDictionary] writeToFile:filePath atomically:YES];
10. Key mapping, 有時, 獲得的數據不是在一個層級,以下:
{
"order_id": 104,
"order_details" : [
{
"name": "Product#1",
"price": {
"usd": 12.95
}
}
]
}
其中的order_id與name就不是一個層級,但咱們仍然想在一個model中獲得它們的數據。 以下:
@interface OrderModel : JSONModel
@property (assign, nonatomic) int id;
@property (assign, nonatomic) float price;
@property (strong, nonatomic) NSString* productName;
@end
@implementation OrderModel
+(JSONKeyMapper*)keyMapper
{
return [[JSONKeyMapper alloc] initWithDictionary:@{
@"order_id": @"id",
@"order_details.name":@"productName",
@"order_details.price.usd":@"price" // 這裏就採用了KVC的方式來取值,它賦給price屬性
}];
}
@end
11. 全局Global key mapping. (使全部的模型都具有), 我的以爲這個並非很是通用,由於若是真是須要全部模型都具有這個keyMapper的轉換,則直接繼承一個基類就好了。
[JSONModel setGlobalKeyMapper:[
[JSONKeyMapper alloc] initWithDictionary:@{
@"item_id":@"ID",
@"item.name":@"itemName"
}]
];
12. 自動把下劃線方式的命名轉爲駝峯命名屬性。還有相似的,如大寫轉爲小寫的方法:mapperFromUpperCaseToLowerCase
{
"order_id": 104,
"order_product" : @"Product#1",
"order_price" : 12.95
}
生成的模型
@interface OrderModel : JSONModel
@property (assign, nonatomic) int orderId;
@property (assign, nonatomic) float orderPrice;
@property (strong, nonatomic) NSString* orderProduct;
@end
@implementation OrderModel
+(JSONKeyMapper*)keyMapper
{
return [JSONKeyMapper mapperFromUnderscoreCaseToCamelCase];
}
@end
13. 可選屬性,建議儘可能使用這種方式來避免異常。
{
"id": "123",
"name": null,
"price": 12.95
}
生成的模型以下:
@interface ProductModel : JSONModel
@property (assign, nonatomic) int id;
@property (strong, nonatomic) NSString<Optional>* name;
@property (assign, nonatomic) float price;
@property (strong, nonatomic) NSNumber<Optional>* uuid;
@end
@implementation ProductModel
@end
經過上面的optional的方式, 咱們能夠給這個類添加一個isSuccess方法,該方法中判斷name和uuid是否存在來決定是否從服務器成功取數據。 而不是把這兩個屬性設置爲required,能夠有效避免異常。
14. Ignore屬性, 會使得解析時會徹底忽略它。 通常狀況下,忽略的屬性主要用在該值不從服務器獲取,而是經過後面的代碼進行設置。
{
"id": "123",
"name": null
}
模型爲:
@interface ProductModel : JSONModel
@property (assign, nonatomic) int id;
@property (strong, nonatomic) NSString<Ignore>* customProperty;
@end
@implementation ProductModel
@end
能夠用下面方法,使當前類的所有屬性都爲可選,官網上說盡可能避免這樣的使用, (我的以爲官網的意思是指,儘可能避免用來面方法來指定全部的屬性爲可選,即便要所有屬性爲可選,也儘可能是在每一個屬性那裏標註爲Optional)
@implementation ProductModel
+(BOOL)propertyIsOptional:(NSString*)propertyName
{
return YES;
}
@end
15. 延遲加載, 這種比較推薦,能夠減小在網絡讀取時的性能消耗:關鍵字爲: ConvertOnDemand
{
"order_id": 104,
"total_price": 103.45,
"products" : [
{
"id": "123",
"name": "Product #1",
"price": 12.95
},
{
"id": "137",
"name": "Product #2",
"price": 82.95
}
]
}
使用模型:
@protocol ProductModel
@end
@interface ProductModel : JSONModel
@property (assign, nonatomic) int id;
@property (strong, nonatomic) NSString* name;
@property (assign, nonatomic) float price;
@end
@implementation ProductModel
@end
@interface OrderModel : JSONModel
@property (assign, nonatomic) int order_id;
@property (assign, nonatomic) float total_price;
@property (strong, nonatomic) NSArray<ProductModel, ConvertOnDemand>* products;
@end
@implementation OrderModel
@end
16. 使用JSONHttpClient進行請求。
//add extra headers
[[JSONHTTPClient requestHeaders] setValue:@"MySecret" forKey:@"AuthorizationToken"];
//make post, get requests
[JSONHTTPClient postJSONFromURLWithString:@"http://mydomain.com/api"
params:@{@"postParam1":@"value1"}
completion:^(id json, JSONModelError *err) {
//check err, process json ...
}];
好了, 因此的JSONModel的使用方法都已經在這裏了, 綜合了官網的使用方法