標籤:
Swift 與 JSON 資料
我們大家平時在開發 App 的時候,相信接觸最多的就是 JSON 資料了。只要你的 App 有讀取網路資料的功能,你就免不了要與 JSON 打交道。比如你做一個新聞 App,你要讀取和解析新聞資料,這樣才能顯示給使用者。
那麼我們今天就來瞭解一下 JSON 以及它在 App 中的應用吧。
在前兩節我們會介紹 JSON 資料格式,如果您已經對 JSON 比較瞭解了,那麼也可以跳過前兩節,繼續閱讀後面的內容。
什麼是 JSON
首先,JSON 的全稱叫做 JavaScript Object Notation ,翻譯成中文就是 JavaScript 物件標記法,是一種輕量級的資料互動格式。
JSON 資料分為三種形式,對象,數組,值。
對象是一個無序的“‘名稱/值’對”集合。一個對象以“{”(左括弧)開始,“}”(右括弧)結束。每個“名稱”後跟一個“:”(冒號);“‘名稱/值’ 對”之間使用“,”(逗號)分隔。
數組是值(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) } } }}
讓我們來逐個講解。
- 首先,我們通過
let url = NSURL(string: APIURL) 來講天氣介面封裝成 NSURL。
- 然後,我們使用
let jsonData = NSData(contentsOfURL: url) 將這個 URL 的內容讀取下載,存放到 NSData 中。
接下來,我們就要使用 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 表示不接受錯誤訊息。
我們得到瞭解析出來的 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,它的取值可以是以下幾種:
MutableContainers: 讓返回的 JSON 資料中的數組和字典是可更改的。
AllowFragments: 允許 JSON 返回的資料有多個根節點。
MutableLeaves: 使 JSON 返回的字串是可更改的。
相信部分膽大心細的朋友會發現。。。
我靠,這說的都是什麼那,我還是不明白!
所以。。客官莫急,聽我一一道來。
首先,MutableContainers 這個選項就讓返回的 JSON 集合可更改,讓我們來看一個例子就一目瞭然了:
var jsonString:NSString = "{\"names\":[\"James\",\"Jobs\",\"Tom\"]}" as NSStringlet 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 這個參數的官方解釋是允許被解析的 JSON 資料的根層級,不是數組和對象。
額。。 聽起來怪怪的不好理解是吧。
這個選項確實容易引起歧義,包括他的名稱 AllowFragments,翻譯成中文叫允許片段, 什麼叫允許片段呢,我再自己實踐研究過這個之前我一直是這麼認為的。。。
AllowFragments 的意思,是不是可以解析這樣的 JSON?
{"name":"Jobs"},{"name":"Ive"}
一段時間以來我的思維裡是這麼想的。可惜完全不是那麼回事兒,如果你將這樣的 JSON 資料傳給 JSONObjectWithData 方法,你將得到一個無情的解析錯誤。。。
那麼,這東西到底是幹什麼用的呀~
其實官方文檔上面說的清清楚楚,可以讓跟節點不是對象或者數組。在 JSON 中只有三種類型,對象,數組,值。
其實說白了就是這樣,允許你的 JSON 資料是一個字面值,比如字串,數字,等等。
比如我們可以傳入一個原始的字串
"something wrong about api"
這種資料,如果你開啟了 AllowFragments, 是完全可以正常解析的(注意兩邊的雙引號,這個也包含在返回的資料中)。而如果你沒有開啟這個選項,對於這種資料就會解析失敗了。
var jsonFragmentString = "\"something wrong about api\"" as NSStringlet 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"]}
區別就在這,這下明白了吧。
Swift & JSON 資料