如何在 Swift 中优雅地处理 JSON
因为Swift对于类型有非常严格的控制,它在处理JSON时是挺麻烦的,因为它天生就是隐式类型。SwiftyJSON是一个能帮助我们在Swift中使用JSON的开源类库。开始之前,让我们先看一下在Swift中处理JSON是多么痛苦。
在Swift中使用JSON的问题
以推ter API为例。使用Swift,从tweet中取得一个用户的“name”值应该非常简单。下面就是我们要处理的JSON:
[ { ...... "text": "just another test", ...... "user": { "name": "OAuth Dancer", "favourites_count": 7, "entities": { "url": { "urls": [ { "expanded_url": null, "url": "http://bit.ly/oauth-dancer", "indices": [ 0, 26 ], "display_url": null } ] } ...... }, "in_reply_to_screen_name": null, }, ......]
在Swift中,你必须这样使用:
let jsonObject : AnyObject! = NSJSONSerialization.JSONObjectWithData(dataFrom推ter, options: NSJSONReadingOptions.MutableContainers, error: nil) if let statusesArray = jsonObject as? NSArray{ if let aStatus = statusesArray[0] as? NSDictionary{ if let user = aStatus["user"] as? NSDictionary{ if let userName = user["name"] as? NSDictionary{ //Finally We Got The Name } } } }
或者,你可以用另外的一个方法,但这不易于阅读:
let jsonObject : AnyObject! = NSJSONSerialization.JSONObjectWithData(dataFrom推ter, options: NSJSONReadingOptions.MutableContainers, error: nil) if let userName = (((jsonObject as? NSArray)?[0] as? NSDictionary)?["user"] as? NSDictionary)?["name"]{ //What A disaster above }
开始
下载在这儿下载SwiftyJSON,或者直接在GitHub克隆它:
git clone https://github.com/lingoer/SwiftyJSON.git
基础用法
SwiftyJSON的使用十分的简单:
典型的NSURLSessionTask抓取推ter的API将产生dataFromNetwork: NSData!:
你首先应该做的事情是初始化JSONValue:
let json = JSONValue(dataFromNetwork)
JSONValue是一个枚举类型表示一个典型的JSON数据结构。
你能使用subscripts检索不同的值从原始的JSONValue中,像这样:
let userName:JSONValue = json[0]["user"]["name"]
注意userName仍然是一个JSONValue。那怎样得到一个字符串呢?
你能用.string属性得到JSON数据表示的真正值。
let userNameString = userName.string!
对每一种JSON类型, JSONValue都提供了一种属性检索它:
var string: String? var number: NSNumber? var bool: Bool? var array: Array<JSONValue>? var object: Dictionary<String, JSONValue>?
注意每一种属性都是一个Optional值。这是因为JSON数据能包含任何它定义的有效类型。
因此,建议的方式是用Optional绑定检索值:
if let name = userName.string{ //This could avoid lots of crashes caused by the unexpected data types } if let name = userName.number{ //As the value of the userName is Not a number. It won't execute. }
.number属性产生一个NSNumber值,在Swift中这通常不是很有用。你能用.double或者.integer得到一个Double值或者一个Int值。
if let intValue = numberValue.integer{ count += intValue }
枚举(Enumeration)
在Swift中JSONValue实际上是一个枚举:
enum JSONValue { case JNumber(NSNumber) case JString(String) case JBool(Bool) case JNull case JArray(Array<JSONValue>) case JObject(Dictionary<String,JSONValue>) case JInvalid(NSError) }
你可以使用一个switch子句去更有效地获取值:
let json = JSONValue(jsonObject) switch json["user_id"]{ case .JString(let stringValue): let id = stringValue.toInt() case .JNumber(let numberValue): let id = numberValue.integerValue default: println("ooops!!! JSON Data is Unexpected or Broken")
下标(Subscripts)
注意,在JSON中一个数组结构被包装成intoArray<JSONVlaue>,它意味着数组里的每一个元素都是一个JSONValue。甚至你从JSONValue中取出一个数组,你仍然可以使用基本的属性去获取元素的值:
if let array = json["key_of_array"].array{ if let string = array[0].string{ //The array[0] is still a JSONValue! } }
对象也是一样。因此,推荐的方式是访问每一个数组和对象时使用JSONValue的下标。
if let string = json["key_of_array"][0].string{ }
实际上,你可以用下标访问一个JSONValue,还不用担心运行时错误导致的崩溃:
let userName = json[99999]["wrong_key"]
如果你使用推荐的方式去取数据,它是安全的:
if let userName = json[99999]["wrong_key"]["name"].string{ //It's always safe }
打印
JSONValue遵守Printable协议.所以很容易在原始字符串中得到JSON数据:
let json = JSONValue(dataFromNetwork) println(json) /*You can get a well printed human readable raw JSON string: { "url": { "urls": [ { "expanded_url": null, "url": "http://bit.ly/oauth-dancer", "indices": [ 0, 26 ], "display_url": null } ] } */
如果你不想打印出来,你可以使用.description属性来得到上述字符串。
let printableString = json.description
调试与错误处理
要是JSON数据出错或者我们错误地检索数据,那会怎么样呢?你可以使用if语句来测试:
let json = JSONValue(dataFromNetworking)["some_key"]["some_wrong_key"]["wrong_name"] if json{ //JSONValue it self conforms to Protocol "LogicValue", with JSONValue.JInvalid stands for false and others stands true }
如果我们尝试使用错误的键值或索引来访问数据,description属性会高数你KeyPath在哪里出错了.
let json = JSONValue(dataFromNetworking)["some_key"]["some_wrong_key"]["wrong_name"] if json{ } else { println(json) //> JSON Keypath Error: Incorrect Keypath "some_wrong_key/wrong_name" //It always tells you where your key went wrong switch json{ case .JInvalid(let error): //An NSError containing detailed error information } }
后记
SwiftyJSON的开发将会发布在Github, 请持续关注后续版本。