如何在 Swift 中優雅地處理 JSON

jopen 10年前發布 | 54K 次閱讀 JSON Apple Swift開發

因為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

            }         }     } }</pre>

或者,你可以用另外的一個方法,但這不易于閱讀:

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. }</pre>

.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)

}</pre>

你可以使用一個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{

}</pre>

實際上,你可以用下標訪問一個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    } }</pre>

后記

 SwiftyJSON的開發將會發布在Github, 請持續關注后續版本。

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!