15

在 Swift Coding/Encoding 功能完成对 JSON 的解码后,我想做一些初始化逻辑。

struct MyStruct: Codable {
    let id: Int 
    var name: String

    init() {
       name = "\(id) \(name)" 
    }
}

但我得到编译器错误:

Return from initializer without initializing all stored properties

这对我来说很清楚,因为 init() 希望我初始化所有属性。但是添加一个具有所有需要的属性的 init() 也不能解决它,因为当 Codable 启动时,这个初始化器不会被调用(!):

init(id: Int, name: String) {
    // This initializer is not called if Decoded from JSON!
    self.id = id 
    self.name = "\(id) \(name)" 
}

尽管如此 - 有没有办法在解码完成后做一些初始化逻辑,但不为每个属性手动完成所有解码?所以不用每次都执行init(from decoder: Decoder)。在这个简短的示例中,我只有两个简单的属性,但生产代码包含数千个。

谢谢。

4

2 回答 2

23

要么您免费获得一切但标准化的东西,要么您必须编写一个自定义初始化程序,例如

struct MyStruct: Codable  {

    let id: Int 
    var name: String

    private enum CodingKeys : String, CodingKey { case id, name }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decode(Int.self, forKey: .id)
        let decodedName = try container.decode(String.self, forKey: .name)
        name = "\(id) \(decodedName)" 
    }
}

您可以实现init(),但这与解码功能无关,您必须为所有非可选属性分配默认值,这就是错误所说的。

于 2018-01-16T13:03:55.693 回答
12

使用首先使用init(from:)然后调用您的自定义初始化代码的工厂方法

struct Foo: Decodable {
    let name: String
    let id: Int

    var x: String!

    private mutating func finalizeInit() {
        self.x = "\(name) \(id)"
    }

    static func createFromJSON(_ data: Data) -> Foo? {
        guard var f = try? JSONDecoder().decode(Foo.self, from: data) else { 
            return nil 
        }
        f.finalizeInit()
        return f
    }
}

let sampleData = """
    { "name": "foo", "id": 42 }
    """.data(using: .utf8)!
let f = Foo.createFromJSON(sampleData)
于 2018-01-16T13:17:18.007 回答