在Swift中默认使类符合Codable [英] Make class conforming to Codable by default in Swift

查看:92
本文介绍了在Swift中默认使类符合Codable的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Swift作为Codable(Decodable& Encodable)协议的这种功能非常有用. 但是我发现了这样的问题: 让我们让类Parent符合Codable:

Such feature of Swift as Codable (Decodable&Encodable) protocol is very useful. But I found such issue: Let we have class Parent conforming to Codable:

class Parent: Codable {
    var name: String
    var email: String?
    var password: String?
}

好吧,该类符合从盒子开始"的Codable协议,您无需编写任何初始化程序,就可以像这样从JSON进行初始化:

Ok, that class is conforming to Codable protocol "from the box", you don't need write any initializers, it's ready to be initialized from JSON like that:

{ "name": "John", "email": "johndoe@yahoo.com", "password": <null>}

但是假设我们需要其他类,Child从Parent继承并符合Codable:

But let's say we need other class, Child inherits from Parent and be conforming to Codable:

class Child: Parent {
   var token: String
   var date: Date?
}

因此,子类别必须通过与父项相符合来使之与Codable相符合, 无法从JSON正确初始化Child类的BUT属性. 我发现的决定是由我自己编写Child类的所有Codable内容,例如:

so class Child must be conforming to Codable by conformance to Parent, BUT properties of class Child won't be initialized from JSON properly. Decision I found is write all Codable stuff for class Child by myself, like:

class Child: Parent {
    var token: String
    var date : Date?

    enum ChildKeys: CodingKey {
        case token, date
    }

    required init(from decoder: Decoder) throws {
        try super.init(from: decoder)
        let container = try decoder.container(keyedBy: ChildKeys.self)
        self.token = try container.decode(String.self, forKey: .token)
        self.date = try container.decodeIfPresent(Date.self, forKey: .date)
    }

    override func encode(to encoder: Encoder) throws {
        try super.encode(to: encoder)
        var container = encoder.container(keyedBy: ChildKeys.self)
        try container.encode(self.token, forKey: .token)
        try container.encodeIfPresent(self.date, forKey: .date)
    }
}

但是我觉得这是不对的,我错过了什么吗?如何在不编写所有内容的情况下使Child类正确地符合Codable?

But I feel it can't be right, did I missed something? How to make class Child conforming to Codable properly without writing all that stuff?

推荐答案

这是一篇不错的博客文章,其中包含您的问题的答案:

Here's a good blog post which includes an answer to your question: source

向下滚动到继承,您将看到以下内容:

假设我们有以下课程:

class Person : Codable {
    var name: String?
}

class Employee : Person {
    var employeeID: String?
}

我们通过继承Person类来获得Codable一致性,但是如果我们尝试对Employee的实例进行编码会发生什么呢?

We get the Codable conformance by inheriting from the Person class, but what happens if we try to encode an instance of Employee?

let employee = Employee()
employee.employeeID = "emp123"
employee.name = "Joe"

let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let data = try! encoder.encode(employee)
print(String(data: data, encoding: .utf8)!)

{
  "name" : "Joe"
}

这不是预期的结果,因此我们必须添加这样的自定义实现:

This is not the expected result, so we have to add a custom implementation like this:

class Person : Codable {
    var name: String?

    private enum CodingKeys : String, CodingKey {
        case name
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(name, forKey: .name)
    }
}

与子类相同的故事:

class Employee : Person {
    var employeeID: String?

    private enum CodingKeys : String, CodingKey {
        case employeeID = "emp_id"
    }

    override func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(employeeID, forKey: .employeeID)
    }
}

结果将是:

{
  "emp_id" : "emp123"
}

这又不是预期结果,因此在这里,我们通过调用 super 使用继承力.

Which again is not the expected result, so here we are using inheritance by calling super.

// Employee.swift
    override func encode(to encoder: Encoder) throws {
        try super.encode(to: encoder)
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(employeeID, forKey: .employeeID)
    }

终于从一开始就给我们真正想要的东西:

Which finally gives us what we really wanted from the beginning:

{
    "name": "Joe",
    "emp_id": "emp123"
}

如果您对展平的结果不满意,那么也有避免这种情况的提示.

If you're not happy with the flattened result, there's a tip on how to avoid that too.

所有致谢撰写博客文章的人和我的感谢. 希望它也对您有帮助,加油!

All the credits to the guy who wrote the blog post and my thanks. Hope it helps you as well, cheers!

这篇关于在Swift中默认使类符合Codable的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆