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
的枚举类型,其中包括 unsupportedType
、indexOutOfBounds
、elementTooDeep
、wrongType
、notExist
和 invalidJSON
,与此同时,ErrorDomain
被 SwiftyJSONError.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。
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 模型的工具: