iOS JSONModel解析數據成Model


轉發自 http://blog.csdn.net/smking/article/details/40432287

 

JSONModel, Mantle

這兩個開源庫都是用來進行封裝JSON->Model的, 想想看, 直接向服務器發起一個請求,然后回來后,就是一個Model, 直接使用, 這是一個多么美好的事情。 感謝GitHub的開源精神。

 

那我們開始吧。 

先說說這兩個的差別。

這兩個使用的方法其實都差不多, 詳細的使用方法請直接GitHub上找, 還是比較簡單地。 就我個人來說JSONModel相對起來使用較為簡單,而Mantle使用起來略為復雜,但是Mantle似乎比JSONModel更為強大。 

一般的項目,其實使用JSONModel就已經足夠。 

 

 下面講一下JSONModel的使用方法。 

@inteface MyModel : JSONModel

 

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];

 

 

[objc]  view plain  copy
  1.   

復雜模型如下, 這里假設復雜模型包含了簡單模型。主要是為了說明模型之前的包含情況下,照樣可以進行解析。

 

[objc]  view plain  copy
  1.   

SuperComplicatedModel* model = [[SuperComplicatedModel alloc] initWithString:@"...json here..." error:nil];

 

模型的批處理,即一次可以處理一批模型。

 

 

[objc]  view plain  copy
  1.   

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

}

 

可以如下定義這個模型

 

 

[objc]  view plain  copy
  1. <span style="color:rgb(166,19,144)"></span>  

@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

 

[objc]  view plain  copy
  1. <span style="color:rgb(166,19,144)"></span>  

用上面這個模型,不需要其它代碼,即可以得到想要的轉換 。

 

這個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的使用方法都已經在這里了, 綜合了官網的使用方法


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM