使用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 } }