?
轉載自:?http://www.cnblogs.com/theswiftworld/p/4660177.html
我們大家平時在開發 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
首先,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 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"]}
區別就在這,這下明白了吧。
其他方案
當然了,除了使用原生的?NSJSONSerialization
?來處理 JSON,我們還可以使用很多第三方庫來進行 JSON 數據的操作,比如:SwiftyJSON,?Argo。
限于篇幅以及大家的閱讀疲勞期,我們這篇文章只介紹原生的方式。后續還會為大家更詳細的講解這些第三方庫的運用,以及他們的好處及缺點。
下面是本篇文章對應的?playground
?文件,大家下載下來應該應該能作為更好的參考:
swift-json.playground