用NSJSONSerialization讓JSON變服帖

之前的文章使用Ono讀取XML文件/簡(jiǎn)書介紹了如何處理XML數(shù)據(jù),隨著Web時(shí)代的ajax的盛行,在App時(shí)代,另一個(gè)最常需要處理的的數(shù)據(jù)就是JSON數(shù)據(jù),尤其現(xiàn)在的Restful背景下API數(shù)據(jù)的傳遞,更有勝者直接就是JSON RPC。此時(shí)既要讀取解析JSON也要為數(shù)據(jù)結(jié)構(gòu)生成對(duì)應(yīng)的JSON數(shù)據(jù)。在以前,iOS/Mac平臺(tái)有多鐘JSON解決方案,如SBJSON、TouchJSON、YAJL、JSONKit,當(dāng)然還有C里面的cJSON/RapidJSON可以進(jìn)行封裝,效率和正確率也是剛剛的。Apple自家也提供了一套解決方案:NSJSONSerialization,這個(gè)NSJSONSerialization比他們自家的XML解析工具NSXMLParser可好多了,他既有可人的接口,又提供了非凡的效率(有測(cè)評(píng)表明比SBJSON/JSONKit等強(qiáng)),并且是Apple系統(tǒng)提供的正規(guī)軍,因此首推這個(gè)方案。

NSJSONSerialization提供的接口非常簡(jiǎn)單,甚至于官網(wǎng)的Manual只有少量的文字描述。

You use the NSJSONSerialization class to convert JSON to Foundation objects and convert Foundation objects to JSON

可見(jiàn),NSJSONSerialization是可以直接將JSON數(shù)據(jù)轉(zhuǎn)換成Foundation中提供的對(duì)象如NSArray、NSDictionary、NSString、NSNumber等;也可以直接把這些Foundation的對(duì)象轉(zhuǎn)換成JSON數(shù)據(jù),這一看就是個(gè)超人性的接口。

但是這么好的接口也有些限制,他要求被解析的JSON數(shù)據(jù)必須是

  • 頂級(jí)對(duì)象需要是一個(gè){}表示的字典或者[]表示的數(shù)組,因此也只能對(duì)NSArray/NSDictionary做序列化。(這點(diǎn)感覺(jué)基本都是這么用的)
  • 所有的節(jié)點(diǎn)數(shù)據(jù)必須要能被解析成 NSString, NSNumber, NSArray, NSDictionary 或者 NSNull.反之也只能對(duì)這些對(duì)象做序列化。
  • 字典的Key必須是NSString表達(dá)的類型。
  • 數(shù)字類型的數(shù)不能是NaN或者無(wú)限大。

總結(jié)來(lái)看其實(shí)只要我們:
在序列化的時(shí)候,只要是對(duì)NSArray或者NSDictionary對(duì)象序列化,并且其單元組成只能是 NSString, NSNumber(NaN/無(wú)限大不可以), NSArray, NSDictionary(key必須是NSString) 或者 NSNull即可。也可以通過(guò)

+ (BOOL)isValidJSONObject:(id)obj

這個(gè)類方法來(lái)判斷其是否可以被正確序列化。

1. 解析JSON數(shù)據(jù)到Foundation對(duì)象

NSJSONSerialization提供了兩個(gè)方法來(lái)解析JSON數(shù)據(jù),一個(gè)是從NSData里面取數(shù)據(jù),另一個(gè)是從NSInputStream 輸入流中取數(shù)據(jù),從而方便對(duì)File/Sokcet的操作。數(shù)據(jù)的格式必須是UTF-8, UTF-16LE, UTF-16BE, UTF-32LE, UTF-32BE中的一種,是否帶有BOM都可以。

+ (nullable id)JSONObjectWithData:(NSData *)data options:(NSJSONReadingOptions)opt error:(NSError **)error;

+ (nullable id)JSONObjectWithStream:(NSInputStream *)stream options:(NSJSONReadingOptions)opt error:(NSError **)error;

兩個(gè)函數(shù)不同的地方就是第一個(gè)參數(shù),一個(gè)傳入的是NSData一個(gè)傳入的是NSInputStream.

options參數(shù)控制讀入的策略:

  • NSJSONReadingMutableContainers : 讀出來(lái)的對(duì)象放入到NSMutableArray或者NSMutableDictionary中,默認(rèn)是放到NSArray/NSDictionary中。這個(gè)選項(xiàng)在我們讀取一段JSON然后進(jìn)行追加或者刪除節(jié)點(diǎn)的時(shí)候,非常適合,修改下Array或者Dictionary成員,然后在序列化就可以了。
  • NSJSONReadingMutableLeaves: 讀出來(lái)的每個(gè)節(jié)點(diǎn)對(duì)象的String放入到NSMutableString中,這樣就為修改節(jié)點(diǎn)提供了可能,在讀入后修改節(jié)點(diǎn)再序列化回去的場(chǎng)景比較適用。
  • NSJSONReadingAllowFragments : 正常情況JSON的頂級(jí)節(jié)點(diǎn)要么是Array[]要么是Dictionary{}。如果JSON數(shù)據(jù)不是通過(guò){}/[]表示的dictionary或者array的片段數(shù)據(jù),這用這個(gè)選項(xiàng)進(jìn)行指定。

error參數(shù)是Cocoa API常用的出錯(cuò)方式,傳入一個(gè)NSError **,如果出錯(cuò),通過(guò)這個(gè)值返回回來(lái)。
解析回來(lái)以后就可以按照NSArray或者NSDictionary的方式進(jìn)行使用了,特別方便。比如:

{
    "id": 1,
    "name": "hanmeimei",
    "class": [
        "English",
        "Mathematics"
    ]
}

相當(dāng)于:

NSArray *myClass = @[@"English",@"Mathematics"];
NSDictionary *student = @{@"id":@1, @"name":@"hmeimei", @"class": myClass};

解析出來(lái)就是一個(gè) student.

2. 將Foundation對(duì)象序列化到JSON中

與上面對(duì)應(yīng)的NSJSONSerialization也提供了兩個(gè)將對(duì)象序列化到JSON的接口,一個(gè)是序列化到NSData中,一個(gè)是序列化到NSOutputStream中,方便對(duì)File/Socket的操作,序列化的結(jié)果是UTF-8格式編碼數(shù)據(jù)。

+ (nullable NSData *)dataWithJSONObject:(id)obj options:(NSJSONWritingOptions)opt error:(NSError **)error;

+ (NSInteger)writeJSONObject:(id)obj toStream:(NSOutputStream *)stream options:(NSJSONWritingOptions)opt error:(NSError **)error;

兩個(gè)函數(shù)不同的地方就是第一個(gè)參數(shù),一個(gè)傳入的是NSData一個(gè)傳入的是NSOutputStream.

默認(rèn)情況下,生成的JSON是盡量壓縮后的結(jié)果,去掉了不必要的空格,可讀性比較差。如果希望生成可讀性較好的格式,比如在調(diào)試的階段,可以使用NSJSONWritingPrettyPrinted 的option參數(shù)為輸出的格式加上必要空格,進(jìn)行格式化。

同樣,如果序列化過(guò)程中出錯(cuò),通過(guò)error參數(shù)進(jìn)行返回。在進(jìn)行序列化的時(shí)候可以使用

+ (BOOL)isValidJSONObject:(id)obj;

判斷是否可以正常序列化。

比如上面的對(duì)student執(zhí)行序列化話,就可以得到上面的JSON數(shù)據(jù)。

3. 一個(gè)示例

最后通過(guò)一個(gè)示例程序看下基本的調(diào)用流程。

JSON文件為students.json:

[
    {
        "id": 1,
        "name": "hanmeimei",
        "class": [
            "English",
            "Mathematics"
        ]
    },
    {
        "id": 2,
        "name": "lilei",
        "class": [
            "English",
            "Mathematics"
        ]
    }
]

程序主要代碼:

NSInputStream *ifs = [[NSInputStream alloc] initWithFileAtPath:@"your_file_path/students.json"];
if (nil == ifs) {
    NSLog(@"ifs is nil");
    return -1;
}
[ifs open];
NSError *error = nil;
NSMutableArray *students = [NSJSONSerialization JSONObjectWithStream:ifs options:NSJSONReadingMutableContainers error:&error];
if (nil != error) {
    NSLog(@"Unparse JSON Error");
}
for (NSDictionary *student in students) {
    NSLog(@"student: %@", [student objectForKey:@"name"]);
    NSLog(@"    select class:");
    for (NSString *cls in [student objectForKey:@"class"]) {
        NSLog(@"        %@\\n", cls);
    }
}

// Parse To
NSDictionary *xiaowang =@{@"id":@3, @"name":@"xiaowang", @"class": @[@"none"]};
[students addObject:xiaowang];
error = nil;
NSData *jsonBuf  = [NSJSONSerialization dataWithJSONObject:students options:NSJSONWritingPrettyPrinted error:&error];
NSString *jsonStr = [[NSString alloc] initWithData:jsonBuf encoding:NSUTF8StringEncoding];
NSLog(@"JSON is %@", jsonStr);

輸出為:

student: hanmeimei
    select class:
         English
         Mathematics
student: lilei
    select class:
           English
           Mathematics
JSON is [
  {
"id" : 1,
"name" : "hanmeimei",
"class" : [
  "English",
  "Mathematics"
]
  },
  {
    "id" : 2,
    "name" : "lilei",
    "class" : [
      "English",
      "Mathematics"
    ]
  },
  {
    "id" : 3,
    "name" : "xiaowang",
    "class" : [
      "none"
    ]
  }
]

可以看到加空格的格式化效果還是比較明顯的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 一、iOS 4種JSON數(shù)據(jù)解析方法詳解 2013年09月10日? 綜合? 共 9124字? 字號(hào)小中大?評(píng)論關(guān)閉...
    lilinjianshu閱讀 2,096評(píng)論 0 2
  • JSON JSON和XML都是需要解析的 JSON是一種輕量級(jí)的數(shù)據(jù)格式,一般用于數(shù)據(jù)交互服務(wù)器返回給客戶端的數(shù)據(jù)...
    JonesCxy閱讀 1,883評(píng)論 2 10
  • NSURLSession基本使用 簡(jiǎn)介 使用步驟使用NSURLSession會(huì)話對(duì)象創(chuàng)建Task,然后執(zhí)行Task...
    彼岸的黑色曼陀羅閱讀 1,025評(píng)論 0 3
  • iOS開(kāi)發(fā)系列--網(wǎng)絡(luò)開(kāi)發(fā) 概覽 大部分應(yīng)用程序都或多或少會(huì)牽扯到網(wǎng)絡(luò)開(kāi)發(fā),例如說(shuō)新浪微博、微信等,這些應(yīng)用本身可...
    lichengjin閱讀 3,721評(píng)論 2 7
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,252評(píng)論 4 61