使用 Apple Foundation 和 Swift 标准库进行 JSON 序列化编码和解码
该 JSONSerialization 类是内置到苹果的基础框架。
Version = 2.2
阅读 JSON
JSONObjectWithData
函数接受 NSData
,并返回 AnyObject
。你可以使用 as?
将结果转换为你期望的类型。
do {
guard let jsonData = "[\"Hello\", \"JSON\"]".dataUsingEncoding(NSUTF8StringEncoding) else {
fatalError("couldn't encode string as UTF-8")
}
// Convert JSON from NSData to AnyObject
let jsonObject = try NSJSONSerialization.JSONObjectWithData(jsonData, options: [])
// Try to convert AnyObject to array of strings
if let stringArray = jsonObject as? [String] {
print("Got array of strings: \(stringArray.joinWithSeparator(", "))")
}
} catch {
print("error reading JSON: \(error)")
}
当顶级对象不是数组或字典时,你可以传递 options: .AllowFragments
而不是 options: []
以允许读取 JSON。
写 JSON
调用 dataWithJSONObject
将与 JSON 兼容的对象(带有字符串,数字和 NSNull
的嵌套数组或字典)转换为以 UTF-8 编码的原始 NSData
。
do {
// Convert object to JSON as NSData
let jsonData = try NSJSONSerialization.dataWithJSONObject(jsonObject, options: [])
print("JSON data: \(jsonData)")
// Convert NSData to String
let jsonString = String(data: jsonData, encoding: NSUTF8StringEncoding)!
print("JSON string: \(jsonString)")
} catch {
print("error writing JSON: \(error)")
}
你可以通过 options: .PrettyPrinted
而不是 options: []
进行漂亮打印。
Version = 3.0
Swift 3 中的行为相同但语法不同。
do {
guard let jsonData = "[\"Hello\", \"JSON\"]".data(using: String.Encoding.utf8) else {
fatalError("couldn't encode string as UTF-8")
}
// Convert JSON from NSData to AnyObject
let jsonObject = try JSONSerialization.jsonObject(with: jsonData, options: [])
// Try to convert AnyObject to array of strings
if let stringArray = jsonObject as? [String] {
print("Got array of strings: \(stringArray.joined(separator: ", "))")
}
} catch {
print("error reading JSON: \(error)")
}
do {
// Convert object to JSON as NSData
let jsonData = try JSONSerialization.data(withJSONObject: jsonObject, options: [])
print("JSON data: \(jsonData)")
// Convert NSData to String
let jsonString = String(data: jsonData, encoding: .utf8)!
print("JSON string: \(jsonString)")
} catch {
print("error writing JSON: \(error)")
}
注意:以下内容目前仅适用于 Swift 4.0 及更高版本。
从 Swift 4.0 开始,Swift 标准库包含 Encodable
和 Decodable
协议,以定义数据编码和解码的标准化方法。采用这些协议将允许 Encoder
和 Decoder
协议的实现获取你的数据,并将其编码或解码到外部表示(如 JSON)和从外部表示解码。符合 Codable
协议结合了 Encodable
和 Decodable
协议。现在,这是在程序中处理 JSON 的推荐方法。
自动编码和解码
使类型可编码的最简单方法是将其属性声明为已经知道的类型。这些类型包括标准库类型,如 String
,Int
和 Double
; 和基础类型,如 Date
,Data
和 URL
。如果类型的属性是可编码的,则类型本身将通过简单地声明一致性自动符合 Codable
。
考虑以下示例,其中 Book
结构符合 Codable
。
struct Book: Codable {
let title: String
let authors: [String]
let publicationDate: Date
}
请注意,如果
Array
和Dictionary
等标准集合包含可编码类型,则它们符合Codable
。
通过采用 Codable
,Book
结构现在可以使用 Apple Foundation 类 JSONEncoder
和 JSONDecoder
编码到 JSON 并从 JSON 解码,即使 Book
本身不包含专门处理 JSON 的代码。也可以通过分别符合 Encoder
和 Decoder
协议来编写自定义编码器和解码器。
编码为 JSON 数据
// Create an instance of Book called book
let encoder = JSONEncoder()
let data = try! encoder.encode(book) // Do not use try! in production code
print(data)
设置
encoder.outputFormatting = .prettyPrinted
以便于阅读。 ##从 JSON 数据解码
从 JSON 数据解码
// Retrieve JSON string from some source
let jsonData = jsonString.data(encoding: .utf8)!
let decoder = JSONDecoder()
let book = try! decoder.decode(Book.self, for: jsonData) // Do not use try! in production code
print(book)
在上面的例子中,
Book.self
通知解码器应该解码 JSON 的类型。
独占编码或解码
有时你可能不需要数据既可编码又可解码,例如当你只需要从 API 读取 JSON 数据时,或者你的程序仅将 JSON 数据提交给 API 时。
如果你只想编写 JSON 数据,请将你的类型与 Encodable
保持一致。
struct Book: Encodable {
let title: String
let authors: [String]
let publicationDate: Date
}
如果你只想阅读 JSON 数据,请将你的类型符合 Decodable
。
struct Book: Decodable {
let title: String
let authors: [String]
let publicationDate: Date
}
使用自定义键名称
API 经常使用除 Swift 标准驼峰案例之外的命名约定,例如蛇案例。在解码 JSON 时,这可能会成为一个问题,因为默认情况下,JSON 键必须与你的类型的属性名称完全对齐。要处理这些情况,你可以使用 CodingKey
协议为你的类型创建自定义键。
struct Book: Codable {
// ...
enum CodingKeys: String, CodingKey {
case title
case authors
case publicationDate = "publication_date"
}
}
CodingKeys
是针对采用 Codable
协议的类型自动生成的,但是通过在上面的示例中创建我们自己的实现,我们允许我们的解码器将本地驼峰情况 publicationDate
与由蛇提供的蛇盒 publication_date
相匹配。