使用Codable进行Swift JSON解析

时间:2020-02-23 14:44:02  来源:igfitidea点击:

在本教程中,我们将讨论可编码协议及其形式,以便在我们的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
  }
}