无需 CodingKeys 的 Swift 自定义可解码初始值设定项

gna*_*ket 8 swift decodable

假设我有以下可解码结构作为示例来说明我正在尝试执行的操作:

struct Object: Decodable {
    var id: String
    var name: String
}
Run Code Online (Sandbox Code Playgroud)

和这个 JSON:

[
    {
        "id": "a",
        "name": "test"
    },
    {
        "id": "b",
        "name": null
    }
]
Run Code Online (Sandbox Code Playgroud)

请注意,名称属性null有时可能是。这基本上可以正常工作,因为 json 键与结构属性名称匹配,所以我不需要枚举CodingKey,但name属性有时可以为 null。但是,name我不想将其设置为可选,而是想替换默认字符串,因此我需要一个自定义初始值设定项:

struct Object: Decodable {
    var id: String
    var name: String

    init(from decoder: Decoder) throws {
        ...
        self.name = <value from decoder> ?? "default name"
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

但这需要一个CodingKey对象。我正在使用默认键。我CodingKey现在还需要一个枚举吗?即使我所有的钥匙都匹配?或者有没有一种方法可以Decodable只使用键本身来自定义初始化程序?

是否有某种我可以使用的默认容器?

let container = try decoder.container(keyedBy: <defaultContainer???>)
Run Code Online (Sandbox Code Playgroud)

我尝试使用这两种变体,但都不起作用:

让容器=尝试解码器.singleValueContainer()

让容器=尝试解码器.unkeyedContainer()

如何拥有自定义Decodable初始化程序同时使用默认键?

Rob*_*Rob 11

问题是,CodingKeys只有在没有\xe2\x80\x99 完全手动遵守相关协议的情况下才会自动生成。(这对于 Objective-C 开发人员来说非常熟悉,如果您手动实现所有相关的访问器方法,则不会自动合成支持 ivar 的 property\xe2\x80\x99s。)

\n\n

所以,在以下场景中,CodingKeys不会自动为您创建:

\n\n
    \n
  1. 您仅采用Decodable并实施您自己的init(from:)

  2. \n
  3. 您仅采用Encodable并实施您自己的encode(to:);或者

  4. \n
  5. 您采用了Encodableand Decodable(或只是Codable)并实现了您自己的init(from:)and encode(to:)

  6. \n
\n\n

所以你的情况属于上面的第一种情况。

\n\n

有人建议您可以通过采用 来解决您的难题Codable,即使您\xe2\x80\x99 只实现了init(from:)并且可能不\xe2\x80\x99 计划使用该Encodable行为。实际上,这依赖于您不打算真正使用的协议的副作用。

\n\n

\xe2\x80\x99t 确实不太重要,并且采用Codable有效,但可能会被认为 \xe2\x80\x9c 更正确\xe2\x80\x9d 继续定义你的CodingKeys,而不是依赖于未实现的Encodable副作用:

\n\n
struct Object: Decodable {\n    var id: String\n    var name: String\n\n    enum CodingKeys: String, CodingKey {\n        case id, name\n    }\n\n    init(from decoder: Decoder) throws {\n        let container = try decoder.container(keyedBy: CodingKeys.self)\n\n        id = try container.decode(String.self, forKey: .id)\n        name = (try? container.decode(String.self, forKey: .name)) ?? "Default Value"\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n