SwiftyJSON 官方 README

50 阅读7分钟

SwiftyJSON 让你在 Swift 中处理 JSON 数据时更容易。

SwiftyJSON 是一个 Swift 框架,旨在消除普通 JSON 序列化中对可选链的需求

1. 为什么 Swift 传统的 JSON 处理方式不好?

Swift 对类型的要求非常严格。尽管明确的类型化有利于我们避免错误,但在处理 JSON 和其他本质上是隐含类型的领域时,它变得非常痛苦。

以 Twitter 的 API 为例。假设我们想在 Swift 中检索某条推文用户的 "name" 值(根据 Twitter 的 API)。

代码会是这样的:

if let statusesArray = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [[String: Any]],
    let user = statusesArray[0]["user"] as? [String: Any],
    let username = user["name"] as? String {
    // Finally we got the username
}

这并不友好。

即使我们使用可选链语法,它也会很乱:

if let JSONObject = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [[String: Any]],
    let username = (JSONObject[0]["user"] as? [String: Any])?["name"] as? String {
        // There's our username
}

一团无法阅读的乱麻 -- 对于真正应该简单的东西来说!

有了 SwiftyJSON,你所要做的就是:

let json = JSON(data: dataFromNetworking)
if let userName = json[0]["user"]["name"].string {
  //Now you got your value
}

SwiftyJSON 消除了检查每个字段的要求,因为如果其中任何一个无效,它将返回 nil

而且不用担心**可选类型解包(Optional Wrapping)**的事情。它会自动为你完成。

let json = JSON(data: dataFromNetworking)
let result = json[999999]["wrong_key"]["wrong_name"]
if let userName = result.string {
    // 放心,“.string”属性仍然可以安全而正确地返回 String? 类型
} else {
    // 打印错误信息
    print(result.error)
}

2. 要求

  • iOS 8.0+ | macOS 10.10+ | tvOS 9.0+ | watchOS 2.0+
  • Xcode 8

3. 集成

你可以使用 CocoaPods 来安装 SwiftyJSON,把它添加到你的 Podfile 中:

platform :ios, '8.0'
use_frameworks!

target 'MyApp' do
    pod 'SwiftyJSON', '~> 4.0'
end

…(略)

4. 用法

初始化

import SwiftyJSON

你可以使用初始化方法创建自己的 JSON 对象:

// 使用 Data 数据创建一个 JSON 对象
let json = JSON(data: dataFromNetworking)

或者:

// 使用一个 Any 对象创建一个 JSON 对象
// 这个对象必须有以下特性: 所有的对象是 NSString/String, NSNumber/Int/Float/Double/Bool, NSArray/Array, NSDictionary/Dictionary, 或 NSNull; 所有的字典的键是 NSStrings/String; NSNumbers 不允许是 NaN 或者 infinity.
let json = JSON(jsonObject) //This could be a string in a JSON format for example

或者:

// 使用 json 字符串创建一个 JSON 对象
if let dataFromString = jsonString.data(using: .utf8, allowLossyConversion: false) {
    let json = JSON(data: dataFromString)
}

下标语法

你可以使用下标语法访问数据:

// Getting a double from a JSON Array
let name = json[0].double
// Getting an array of string from a JSON Array
let arrayNames =  json["users"].arrayValue.map { $0["name"].stringValue }
// Getting a string from a JSON Dictionary
let name = json["name"].stringValue

你可以把你的路径编译成一个 Swift Array:

// Getting a string using a path to the element
// 使用元素的路径获得一个字符串值
let path: [JSONSubscriptType] = [1,"list",2,"name"]
let name = json[path].string
// 效果同上
let name = json[1]["list"][2]["name"].string
// 效果同上
let name = json[1,"list",2,"name"].string
// With a hard way
let name = json[].string
// With a custom way
let keys:[JSONSubscriptType] = [1,"list",2,"name"]
let name = json[keys].string

Loop

// If json is .Dictionary
for (key,subJson):(String, JSON) in json {
   // Do something you want
}

第一个元素总是一个 String,即使 JSON 是一个 Array。

// If json is .Array
// The `index` is 0..<json.count's string value
for (index,subJson):(String, JSON) in json {
    // Do something you want
}

Error

SwiftyJSON 4.x

SwiftyJSON 4.x 引入了一个名为 SwiftyJSONError 的枚举类型,其中包括 unsupportedTypeindexOutOfBoundselementTooDeepwrongTypenotExistinvalidJSON,与此同时,ErrorDomainSwiftyJSONError.errorDomain 取代。注意:这些旧的错误类型在 SwiftyJSON 4.x 中被废弃,并将在未来的版本中被移除。

SwiftyJSON 3.x

使用下标语法来获取 / 设置数组或字典中的一个值

如果 JSON 是:

  • 数组,应用程序可能会因"index out-of-bounds"索引越界而崩溃。
  • 字典,它将被无缘无故地分配为 nil
  • 既不是数组也不是字典,应用程序可能会因 "unrecognised selector" 异常而崩溃。

这种情况在 SwiftyJSON 中不会发生。

let json = JSON(["name", "age"])
if let name = json[999].string {
    // Do something you want
} else {
    print(json[999].error!) // "Array[999] is out of bounds"
}
let json = JSON(["name":"Jack", "age": 25])
if let name = json["address"].string {
    // Do something you want
} else {
    print(json["address"].error!) // "Dictionary["address"] does not exist"
}
let json = JSON(12345)
if let age = json[0].string {
    // Do something you want
} else {
    print(json[0])       // "Array[0] failure, It is not an array"
    print(json[0].error!) // "Array[0] failure, It is not an array"
}

if let name = json["name"].string {
    // Do something you want
} else {
    print(json["name"])       // "Dictionary[\"name"] failure, It is not an dictionary"
    print(json["name"].error!) // "Dictionary[\"name"] failure, It is not an dictionary"
}

Optional getter

你可以将值解析为某种数据类型,这将返回一个可选类型

// NSNumber
if let id = json["user"]["favourites_count"].**number** {
   // Do something you want
} else {
   // **SwiftyJSON 具有打印自己的错误的功能**
   print(json["user"]["favourites_count"].error!)
}
// String
if let id = json["user"]["name"].**string** {
   // Do something you want
} else {
   // Print the error
   print(json["user"]["name"].error!)
}
// Bool
if let id = json["user"]["is_translator"].**bool** {
   // Do something you want
} else {
   // Print the error
   print(json["user"]["is_translator"].error!)
}
// Int
if let id = json["user"]["id"].**int** {
   // Do something you want
} else {
   // Print the error
   print(json["user"]["id"].error!)
}
...

Non-optional getter

非可选类型的 getter 方法被命名为 xxxValue

// If not a Number or nil, return 0
let id: Int = json["id"].intValue
// If not a String or nil, return ""
let name: String = json["name"].stringValue
// If not an Array or nil, return []
let list: Array<JSON> = json["list"].arrayValue
// If not a Dictionary or nil, return [:]
let user: Dictionary<String, JSON> = json["user"].dictionaryValue

Setter

可以使用下标语法写入 JSON 对象:

json["name"] = JSON("new-name")
json[0] = JSON(1)
json["id"].int =  1234567890
json["coordinate"].double =  8766.766
json["name"].string =  "Jack"
json.arrayObject = [1,2,3,4]
json.dictionaryObject = ["name":"Jack", "age":25]

Row object

let rawObject: Any = json.object
let rawValue: Any = json.rawValue
//convert the JSON to raw NSData
do {
	let rawData = try json.rawData() // 这是一个 NSData 对象
  // Send the data to your server if you like
} catch {
	print("Error \(error)")
}

如果你需要 JSON 的原始字符串。比如你需要把它写入文件,则可以获取原始值:

//convert the JSON to a raw String
if let rawString = json.rawString() { // 这是一个 String 对象
  // Write the string to a file if you like
} else {
	print("json.rawString is nil")
}

Existence 存在性

// shows you whether value specified in JSON or not
// 向你显示在 JSON 中指定的值是否存在
if json["name"].exists()

字面量转换

关于字面转换的更多信息: Swift Literals

// StringLiteralConvertible
let json: JSON = "I'm a json"
// IntegerLiteralConvertible
let json: JSON =  12345
// BooleanLiteralConvertible
let json: JSON =  true
// FloatLiteralConvertible
let json: JSON =  2.8765
// DictionaryLiteralConvertible
let json: JSON =  ["I":"am", "a":"json"]
// ArrayLiteralConvertible
let json: JSON =  ["I", "am", "a", "json"]
// With subscript in array
var json: JSON =  [1,2,3]
json[0] = 100
json[1] = 200
json[2] = 300
json[999] = 300 // Don't worry, nothing will happen
// With subscript in dictionary
var json: JSON =  ["name": "Jack", "age": 25]
json["name"] = "Mike"
json["age"] = "25" // It's OK to set String
json["address"] = "L.A." // Add the "address": "L.A." in json
// Array & Dictionary
var json: JSON =  ["name": "Jack", "age": 25, "list": ["a", "b", "c", ["what": "this"]]]
json["list"][3]["what"] = "that"
json["list",3,"what"] = "that"
let path: [JSONSubscriptType] = ["list",3,"what"]
json[path] = "that"
// With other JSON objects
let user: JSON = ["username" : "Steve", "password": "supersecurepassword"]
let auth: JSON = [
  "user": user.object, // use user.object instead of just user
  "apikey": "supersecretapitoken"
]

Merging 合并

可以将一个 JSON 合并到另一个 JSON 中。将一个 JSON 合并到另一个 JSON 中会将所有不存在的值添加到原始 JSON 中,而这些值只存在于另一个 JSON 中。

如果两个 JSON 都包含相同的 key 值,这个值大多会在原始 JSON 中被覆盖,但有两种情况下它会提供一些特殊处理:

  • 如果两个值都是 JSON.Type.array,在另一个 JSON 中找到的数组的值会被附加到原始 JSON 的数组值上。
  • 如果两个值都是 JSON.Type.dictionary,两个 JSON 值都会被合并,就像封装的 JSON 被合并一样。

在 JSON 中的两个字段有不同类型的情况下,值将总是被覆盖的。

有两种不同的合并方式:合并会修改原始的 JSON,而合并则以非破坏性的方式工作在副本上。

let original: JSON = [
    "first_name": "John",
    "age": 20,
    "skills": ["Coding", "Reading"],
    "address": [
        "street": "Front St",
        "zip": "12345",
    ]
]

let update: JSON = [
    "last_name": "Doe",
    "age": 21,
    "skills": ["Writing"],
    "address": [
        "zip": "12342",
        "city": "New York City"
    ]
]

let updated = original.merge(with: update)
// [
//     "first_name": "John",
//     "last_name": "Doe",
//     "age": 21, // key 同名时,原始 JSON 中的值被覆盖
//     "skills": ["Coding", "Reading", "Writing"], // key 同名时,原始数组被附加
//     "address": [ // key 同名时,原始字典被合并+覆盖
//         "street": "Front St",
//         "zip": "12342",
//         "city": "New York City"
//     ]
// ]

5. String representation(字符串表示法

有两个选项可用:

  • 使用默认的 Swift 字符串;
  • 使用自定义方法,可以很好地处理可选类型并将 nil 表示为 "null"
let dict = ["1":2, "2":"two", "3": nil] as [String: Any?]
let json = JSON(dict)
let representation = json.rawString(options: [.castNilToNSNull: true])
// representation is "{\"1\":2,\"2\":\"two\",\"3\":null}", which represents {"1":2,"2":"two","3":null}

6. 与 Alamofire 协作

SwiftyJSON 很好地包装了 Alamofire JSON 响应处理的结果:

Alamofire.request(url, method: .get).validate().responseJSON { response in
    switch response.result {
    case .success(let value):
        **let json = JSON(value)**
        print("JSON: \(json)")
    case .failure(let error):
        print(error)
    }
}

我们还提供了一个 Alamofire 扩展,用于将 NSData 序列化为 SwiftyJSON 的 JSON。

见:github.com/SwiftyJSON/…

7. 与 Moya 协作

SwiftyJSON 解析数据到 JSON:

let provider = MoyaProvider<Backend>()
provider.request(.showProducts) { result in
    switch result {
    case let .success(moyaResponse):
        let data = moyaResponse.data
        **let json = JSON(data: data)** // convert network data to json
        print(json)
    case let .failure(error):
        print("error: \(error)")
    }
}

8. SwiftyJSON 模型生成器

生成 SwiftyJSON 模型的工具: