【问题标题】:How to use JSONDecoder with inheritance / protocols?如何将 JSONDecoder 与继承/协议一起使用?
【发布时间】:2018-04-24 13:56:43
【问题描述】:

假设我在 JSON 中混合了一个 employeesemployers 数组。两者都继承自Person。在 JSONDecoder 中处理它的正确方法是什么?像这样的东西是行不通的,因为我们不能将它转换回子类:

let decoder = JSONDecoder()
let persons = try! decoder.decode([Person].self, for: jsonData)

还有一点:我们可以在这里使用协议而不是超类吗?

这就是我的示例 JSON 的样子:

[
{
    "id": 1,
    "type": "employee",
    "employee_name": "xy"
},
{
    "id": 2,
    "type": "employer",
    "employer_name": "xz"
}
]

【问题讨论】:

标签: ios json swift parsing codable


【解决方案1】:

首先让我们将您的 json 转换为 Data

let data = """
[
    {
        "id": 1,
        "type": "employee",
        "employee_name": "xy"
    },
    {
        "id": 2,
        "type": "employer",
        "employer_name": "xz"
    }
]
""".data(using: .utf8)!

重要提示:替换“!”使用更安全的展开方法

型号

现在我们需要一个模型值来匹配 JSON 中的元素

struct ResponseElement:Codable {
    let id: Int
    let type: Type
    let employeeName: String?
    let employerName: String?

    enum CodingKeys: String, CodingKey {
        case id, type, employeeName = "employee_name", employerName = "employer_name"
    }

    enum `Type`:String, Codable {
        case employee, employer
    }
}

如您所见,employeeNameemployerName可选项,因此该结构将能够保存 JSON 的每个元素(EmployersEmployee)。

Person 及其子类

假设您有这样的课程Person

class Person {
    let id: Int
    let name:String

    init(id:Int, name:String) {
        self.id = id
        self.name = name
    }
}

您需要像这样创建 EmployeeEmpolyer 子类

class Employee:Person {
    init?(responseElement:ResponseElement) {
        guard let name = responseElement.employeeName, responseElement.type == .employee else { return nil }
        super.init(id: responseElement.id, name: name)
    }
}
class Employer:Person {
    init?(responseElement:ResponseElement) {
        guard let name = responseElement.employerName, responseElement.type == .employer else { return nil }
        super.init(id: responseElement.id, name: name)
    }
}

请注意,Employee 有一个可失败的初始化程序,它将尝试从 ResponseElement 创建一个 EmployeeEmployer 也一样。

让我们解码!

do {
    let elements = try JSONDecoder().decode([ResponseElement].self, from: data)
    let employees = elements.filter { $0.type == .employee }.flatMap(Employee.init)
    let employers = elements.filter { $0.type == .employer }.flatMap(Employer.init)

    print(employees.count)
    print(employers.count)
} catch {
    print("Something bad has happened ?:\(error)")
}

【讨论】:

  • 嘿卢卡感谢您的解决方案。它肯定会起作用,但 ResponseElement 似乎对其他模型类了解太多。只有 2 个模型不是问题,但是拥有 5-10 个模型,每个模型有 5-10 个字段会导致样板代码 IMO。请参阅我在 Github 上找到的有趣解决方案的答案。
【解决方案2】:

我刚刚为这个问题找到了一个不错的solution

【讨论】:

  • 你好@cocoapriest,请注意 StackOverflow discourages 答案只包含一个链接。请考虑扩展您的答案。
猜你喜欢
  • 2018-05-12
  • 1970-01-01
  • 1970-01-01
  • 2014-03-03
  • 1970-01-01
  • 2011-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多