使用Codable进行Swift JSON解析
在本教程中,我们将讨论可编码协议及其形式,以便在我们的Swift Playground中解析/序列化JSON。
我们之前已经讨论了使用JSONSerialization进行Swift JSON解析。
Swift编码协议
Swift4中引入了可编码协议,它很好地替代了NSCoding。
让我们回顾一下NSCoding。
Swift NSCoding
NSCoding协议是基础框架的一部分,出于相同的目的已经存在,即分别将数据编码到JSON和从JSON解码数据。
让我们在XCode Playground中使用NSCoding。
class Student : NSObject, NSCoding
{
var name: String?
var age: Int?
required init?(coder aDecoder: NSCoder)
{
//Returns an object initialized from data in a provided unarchiver.
self.name = aDecoder.decodeObject(forKey: "name") as? String
self.age = aDecoder.decodeObject(forKey: "age") as? Int
}
init(name:String,age:Int) {
self.name = name
self.age = age
}
func encode(with aCoder: NSCoder)
{
//Encodes the given object using provided archiver.
aCoder.encode(self.name, forKey: "name")
aCoder.encode(self.age, forKey: "age")
}
override var description: String {
get {
return "[ name=\(self.name) ; age=\(self.age) ]"
}
}
}
由于我们使用了NSCoding协议,因此必须将" encode"连同解码器的init功能一起实现。
对于序列化,我们使用类NSKeyedArchiver:
let student = Student(name: "Anupam", age: 24)
let data = NSKeyedArchiver.archivedData(withRootObject: student)
let decoded = NSKeyedUnarchiver.unarchiveObject(with: data) as! Student
print(decoded)
//Prints [ name=Optional("Anupam") ; age=Optional(24) ]
尝试在任何函数中更改键名,都会返回nil。
NSCoding对于通过将对象图保存在Archiver中来保存应用程序状态非常有用。
话虽如此,NSCoding也有其缺点:
除Swift中的类外,不能与其他任何东西一起使用。
编码和解码的冗余代码过多。
需要为每个字段添加。
Apple意识到了这些缺点,并引入了Codable协议以加快开发速度!
可编码协议是两个协议的合并:"可编码"和"可解码"。
Codable是一种类型别名:
typealias Codable = Encodable & Decodable
这些协议与Swift类,struct,enums一起使用。
另一个协议CodingKey用于定义我们自己的自定义密钥。
我们可以通过为它们分配默认值来省略某些值。
可编码协议将自定义类型编码为数据。
数据可以是plist或者JSON。
Encodable使用encode(to:)函数。
可解码将数据转换回自定义类型。
Decodable使用init(from:)函数。
JSONEncoder和JSONDecoder用于JSON数据
PropertyListEncoder和PropertyListDecoder用于plist数据。
编码和解码JSON数据
enum Section: String, Codable
{
case A
case B
case C
}
class Student: NSObject, Codable
{
var name: String = ""
var id: URL? = nil
var year: Int = 0
var isNew:Bool = true
var peer: [String:String]? = nil
var section: Section = .A
}
let student = Student()
student.name = "Anupam"
student.year = 2011
student.id = URL(string: "https://www.theitroad.local")
student.section = .B
let encodedObject = try? JSONEncoder().encode(student)
if let encodedObjectJsonString = String(data: encodedObject!, encoding: .utf8)
{
print(encodedObjectJsonString)
}
要解码JSON字符串,请执行以下操作:
let jsonString = """
{
"name":"Anupam",
"isNew":true,
"year":2016,
"id":"j.com",
"section":"A"
}
"""
if let jsonData = jsonString.data(using: .utf8)
{
let studentObject = try? JSONDecoder().decode(Student.self, from: jsonData)
}
解码JSON数组
let jsonString = """
[{
"name":"Anupam",
"isNew":true,
"year":2016,
"id":"j.com",
"section":"A"
},
{
"name":"Anupam",
"isNew":true,
"year":2011,
"id":"j.com",
"section":"C"
}]
"""
if let jsonData = jsonString.data(using: .utf8)
{
let studentObject = try? JSONDecoder().decode([Student].self, from: jsonData)
print(studentObject?.count)
}
如果传递给解码器的json字符串不具有所有属性,则它将返回nil实例。
删除不必要的属性
使用CodingKey协议,我们可以决定要编码或者解码的属性。
enum Section: String, Codable
{
case A
case B
case C
}
class Student: NSObject, Codable
{
var name: String = ""
var id: URL? = nil
var year: Int = 0
var isNew:Bool = true
var peer: [String:String]? = nil
var section: Section = .A
enum CodingKeys:String,CodingKey
{
case name
case id
}
}
let student = Student()
student.name = "Anupam"
student.year = 2011
student.id = URL(string: "https://www.theitroad.local")
student.section = .B
输出:
{"name":"Anupam","id":"https:\/\/www.theitroad.local"}
只有通过的案例才被编码。
使用自定义键名
同样,使用CodingKey协议将自定义密钥名称分配给将要编码和解码的属性。
enum Section: String, Codable
{
case A
case B
case C
}
class Student: NSObject, Codable
{
var name: String = ""
var id: URL? = nil
var year: Int = 0
var isNew:Bool = true
var peer: [String:String]? = nil
var section: Section = .A
enum CodingKeys: String, CodingKey {
case name = "user_name"
case id = "user_id"
case year
case isNew = "is_new"
case peer
case section
}
}

