Swift 與 JSON 數據

Swift 與 JSON 數據

 

咱們你們平時在開發 App 的時候,相信接觸最多的就是 JSON 數據了。只要你的 App 有讀取網絡數據的功能,你就免不了要與 JSON 打交道。好比你作一個新聞 App,你要讀取和解析新聞數據,這樣才能顯示給用戶。html

那麼咱們今天就來了解一下 JSON 以及它在 App 中的應用吧。git

在前兩節咱們會介紹 JSON 數據格式,若是您已經對 JSON 比較瞭解了,那麼也能夠跳過前兩節,繼續閱讀後面的內容。github

什麼是 JSON

首先,JSON 的全稱叫作 JavaScript Object Notation ,翻譯成中文就是 JavaScript 對象表示法,是一種輕量級的數據交互格式。json

JSON 數據分爲三種形式,對象,數組,值。api

對象是一個無序的「‘名稱/值’對」集合。一個對象以「{」(左括號)開始,「}」(右括號)結束。每一個「名稱」後跟一個「:」(冒號);「‘名稱/值’ 對」之間使用「,」(逗號)分隔。數組

數組是值(value)的有序集合。一個數組以「[」(左中括號)開始,「]」(右中括號)結束。值之間使用「,」(逗號)分隔。瀏覽器

值(value)能夠是雙引號括起來的字符串(string)、數值(number)、true、false、 null、對象(object)或者數組(array)。這些結構能夠嵌套。網絡

下面是一個簡單的例子:數據結構

{
  "firstname": "San",
  "lastname" : "Zhang",
  "age": 21,
  "friends": ["Mark","Li"]
}

上面的數據示例,表示了這樣一個結構,首先咱們的數據被一對大括號包圍,那麼咱們的數據就是 對象 類型,而後它裏面有四個屬性,firstname,lastname,age,friends。 其中前兩個屬性 firstname 和 lastname 字符串類型,他們的值分別是 San 和 Zhang。 age 屬性表明年齡,因此它的值一個 Number 類型的 21工具

注意一下,字符串類型和數字類型的區別,字符串類型的值用一對雙引號括了起來,而數值類型不須要雙引號。

最後,friends 屬性的值是一個數組,用一對中括號包圍起來,而數組中的元素,仍然是字符串類型。

以上就是 JSON 的一個基本結構,關於 JSON 更詳細的介紹,能夠參看 json.org

JSON 數據實例

咱們看完了 JSON 的格式以後,那麼咱們就接着看一下具體的 JSON 數據是怎樣的格式呢?

好比這個天氣數據接口: http://api.openweathermap.org/data/2.5/weather?q=China,bj&lang=zh_cn

若是咱們在瀏覽器打開這個地址,咱們就能夠看到相似這樣的數據:

{
  "coord": {
    "lon": 116.4,
    "lat": 39.91
  },
  "weather": [
    {
      "id": 520,
      "main": "Rain",
      "description": "陣雨",
      "icon": "09d"
    },
    {
      "id": 701,
      "main": "Mist",
      "description": "薄霧",
      "icon": "50d"
    }
  ],
  "base": "stations",
  "main": {
    "temp": 300.39,
    "pressure": 1008,
    "humidity": 94,
    "temp_min": 297.15,
    "temp_max": 303.71
  },
  "visibility": 2300,
  "wind": {
    "speed": 1,
    "deg": 140
  },
  "clouds": {
    "all": 75
  },
  "dt": 1437281131,
  "sys": {
    "type": 1,
    "id": 7405,
    "message": 0.0136,
    "country": "CN",
    "sunrise": 1437253268,
    "sunset": 1437305986
  },
  "id": 1816670,
  "name": "Beijing",
  "cod": 200
}

咱們來簡單看一下,數據總體使用一對大括號包圍的,也就是說返回給咱們的數據,是一個 JSON 對象 緊接着,這個對象包含了 coord 屬性,這個屬性的值又是一個對象,裏面有兩個屬性 'lon' 和 'lat' 表明地理位置,後面還有不少其餘屬性表明天氣的數據。

JSON 數據格式,能夠很結構化的表示出天氣的信息。並且數據結構一目瞭然,很是的清晰。而且有不少在線工具能夠幫助你們更好的編輯和查看 JSON 數據。
好比 http://www.jsoneditoronline.org

Swift 中處理 JSON 數據

咱們在瞭解過 JSON 數據後,就繼續咱們的主題吧。

使用 NSJSONSerialization

Swift 中處理 JSON 數據方式有不少種。首先,因爲 Swift 能夠引用 Cocoa 原生庫,因此咱們能夠用 Cocoa 中的 NSJSONSerialization 來處理 JSON 數據,這個類也很好理解,它會將 JSON 數據,轉換成 Cocoa 中的 NSDictionary 和 NSArray。咱們來看一下如何用 NSJSONSerialization 來處理:

let APIURL = "http://api.openweathermap.org/data/2.5/weather?q=China,bj&lang=zh_cn"

if let url = NSURL(string: APIURL) {

    if let jsonData = NSData(contentsOfURL: url) {

        if let jsonObj:NSDictionary = NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions.allZeros, error: nil) as? NSDictionary{

            if let weathers:NSArray = jsonObj["weather"] as? NSArray {

                var weatherSummary = "北京天氣狀況:"

                for weather in weathers {

                    if let desc:String = weather["description"] as? String {

                      weatherSummary += desc + " "

                    }

                }

                print(weatherSummary)

            }

        }

    }

}

讓咱們來逐個講解。

  1. 首先,咱們經過 let url = NSURL(string: APIURL) 來說天氣接口包裝成 NSURL。
  2. 而後,咱們使用 let jsonData = NSData(contentsOfURL: url) 將這個 URL 的內容讀取下載,存放到 NSData 中。
  3. 接下來,咱們就要使用 NSJSONSerialization 將這些數據解析成 JSON 了。
    let jsonObj:NSDictionary = NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions.allZeros, error: nil) as? NSDictionary
    這裏咱們 JSONObjectWithData 方法將傳入的 NSData 數據解析成 JSON 對象,若是咱們的 JSON 根節點是以對象形式存放的,那麼咱們獲得的就是一個 NSDictionary。而若是是以數組形式存放的,那麼咱們獲得的就是一個 NSArray 了。後面還有兩個參數 options 表明 JSON 讀取選項,這個咱們稍後會講到,error 參數表示 JSON 讀取中的錯誤,若是傳入 nil 表示不接受錯誤消息。

  4. 咱們獲得瞭解析出來的 JSON 後,咱們就能夠像訪問普通集合對象那樣獲得裏面的信息了:

if let weathers:NSArray = jsonObj["weather"] as? NSArray {

    var weatherSummary = "北京天氣狀況:"

    for weather in weathers {

        if let desc:String = weather["description"] as? String {

          weatherSummary += desc + " "

        }

    }

    print(weatherSummary)

}

咱們這裏將天氣狀況讀取出來,並打印到屏幕上,以咱們上面的數據爲例,打印到屏幕上就是這個樣子:

北京天氣狀況:陣雨 薄霧

NSJSONSerialization 的讀取選項

就在剛剛,咱們使用 NSJSONSerialization 成功的解析了 JSON 數據,以爲用起來很爽吧。仔細回想一下,咱們剛纔還注意到有一個 options 參數咱們沒有詳細介紹。這個咱們能夠把它叫作讀取選項,這個參數的類型是 NSJSONReadingOptions,它的取值能夠是如下幾種:

  1. MutableContainers: 讓返回的 JSON 數據中的數組和字典是可更改的。
  2. AllowFragments: 容許 JSON 返回的數據有多個根節點。
  3. MutableLeaves: 使 JSON 返回的字符串是可更改的。

相信部分膽大心細的朋友會發現。。。

我靠,這說的都是什麼那,我仍是不明白!

因此。。客官莫急,聽我一一道來。

  • MutableContainers

首先,MutableContainers 這個選項就讓返回的 JSON 集合可更改,讓咱們來看一個例子就一目瞭然了:

var jsonString:NSString = "{\"names\":[\"James\",\"Jobs\",\"Tom\"]}" as NSString
let jsonData = jsonString.dataUsingEncoding(NSUTF8StringEncoding)

if let jsonObj:NSDictionary = NSJSONSerialization.JSONObjectWithData(jsonData!, options: NSJSONReadingOptions.MutableContainers, error: nil) as? NSDictionary {

    //操做以前
    print(jsonObj) //James, Jobs, Tom

    if let nameArray:NSMutableArray = jsonObj["names"] as? NSMutableArray {

        nameArray.addObject("Cook")

    }

    //操做以後
    print(jsonObj) //James, Jobs, Tom, Cook

}

咱們看一下吧,上面的代碼,咱們在 JSONObjectWithData 方法調用的時候,加入了 NSJSONReadingOptions.MutableContainers 讀取參數, 這樣一來咱們就能夠更改咱們的結果集了,咱們注意到上面的這段代碼:

if let nameArray:NSMutableArray = jsonObj["names"] as? NSMutableArray {

       nameArray.addObject("Cook")

}

取得 names 數組,並在裏面增長一個新的項。隨後咱們再次打印 jsonObj 對象,此次顯示的結果就是咱們更改過的了。

若是咱們在調用 JSONObjectWithData 方法的時候將 NSJSONReadingOptions.MutableContainers 選項去掉的話,咱們就不能更改這裏面任何數組的元素了。

第一個選項 MutableContainers 咱們看完啦。 咱們繼續

  • AllowFragments

還有另一個選項參數,就是 AllowFragments 這個參數的官方解釋是容許被解析的 JSON 數據的根層級,不是數組和對象。

額。。 聽起來怪怪的很差理解是吧。

這個選項確實容易引發歧義,包括他的名稱 AllowFragments,翻譯成中文叫容許碎片, 什麼叫容許碎片呢,我再本身實踐研究過這個以前我一直是這麼認爲的。。。

AllowFragments 的意思,是否是能夠解析這樣的 JSON?

{"name":"Jobs"},{"name":"Ive"}

一段時間以來個人思惟裏是這麼想的。惋惜徹底不是那麼回事兒,若是你將這樣的 JSON 數據傳給 JSONObjectWithData 方法,你將獲得一個無情的解析錯誤。。。

那麼,這東西究竟是幹什麼用的呀~

其實官方文檔上面說的清清楚楚,可讓跟節點不是對象或者數組。在 JSON 中只有三種類型,對象,數組,值。

其實說白了就是這樣,容許你的 JSON 數據是一個字面值,好比字符串,數字,等等。

好比咱們能夠傳入一個原始的字符串

"something wrong about api"

這種數據,若是你打開了 AllowFragments, 是徹底能夠正常解析的(注意兩邊的雙引號,這個也包含在返回的數據中)。而若是你沒有打開這個選項,對於這種數據就會解析失敗了。

var jsonFragmentString = "\"something wrong about api\"" as NSString

let jsonFragmentData = jsonFragmentString.dataUsingEncoding(NSUTF8StringEncoding)

if let jsonObj: AnyObject = NSJSONSerialization.JSONObjectWithData(jsonFragmentData!, options: .AllowFragments, error: nil) {

    //使用 AllowFragments 選項,解析成功。
    print(jsonObj)

}

看完上面的代碼,相信你們瞬間就明白了,原來這傢伙是作這個用的。

  • MutableLeaves 
    MutableLeaves 選項,這個選項讓我一直百思不得其解,文檔上說,使用了這個選項後,全部對象的葉子節點的字符串屬性,都會變成 NSMutableString,而我試遍多種文檔,也未驗證出來,獲得的字符串依然是 NSString 而不是 NSMutableString

使用 NSJSONSerialization 建立 JSON 數據

剛纔咱們瞭解到如何用 NSJSONSerialization 來解析數據。一樣的,咱們還可使用 NSJSONSerialization 來構建 JSON 數據。

讓咱們看一下下面的代碼:

let names = ["Jobs","Cook","Ive"]

if let jsonData = NSJSONSerialization.dataWithJSONObject(names, options: NSJSONWritingOptions.allZeros, error: nil) {
    let jsonString = NSString(data: jsonData, encoding: NSUTF8StringEncoding)
    // ["Jobs","Cook","Ive"]
}

咱們使用 dataWithJSONObject 方法將 JSON 對象轉換成 JSON 數據,咱們傳入的對象能夠是數組也能夠是字典,分別對應了 JSON 中的數組和對象。

咱們注意到 dataWithJSONObject 這個方法也有個 options 選項,它用來控制構建 JSON 時的選項,類型爲 NSJSONWritingOptions。它只有一個選項,就是 NSJSONWritingOptions.PrettyPrinted

這個選項的做用也不言而喻,就是讓生成的 JSON 數據是良好的格式化的:

let jsonObj = ["name":"Jobs","friends":["Ive","Cook"]]

if let jsonData = NSJSONSerialization.dataWithJSONObject(jsonObj, options: NSJSONWritingOptions.PrettyPrinted, error: nil) {

    let jsonString = NSString(data: jsonData, encoding: NSUTF8StringEncoding)

}

使用 PrettyPrinted 選項,咱們輸出的 JSON 就是這樣一個良好格式化的:

{
  "name": "Jobs",
  "friends": [
    "Ive",
    "Cook"
  ]
}

若是咱們沒有使用這個選項,那麼咱們獲得的輸出就是這樣:

{"name": "Jobs","friends": ["Ive","Cook"]}

區別就在這,這下明白了吧。

其餘方案

固然了,除了使用原生的 NSJSONSerialization 來處理 JSON,咱們還可使用不少第三方庫來進行 JSON 數據的操做,好比:SwiftyJSONArgo

限於篇幅以及你們的閱讀疲勞期,咱們這篇文章只介紹原生的方式。後續還會爲你們更詳細的講解這些第三方庫的運用,以及他們的好處及缺點。

下面是本篇文章對應的 playground 文件,你們下載下來應該應該能做爲更好的參考:

相關文章
相關標籤/搜索