繁体   English   中英

覆盖结构中的 Codable 实现

[英]Override Codable implementation in struct

有没有办法覆盖结构的 Codable 实现?

无法更改的代码:

struct obj1 : Codable {
   var values:[Int]
   ...
}

所需的 JSON:

{ "x": 1, "y": 2, "z": 3 }

这不起作用:

extension obj1 {
   init(from decoder: Decoder) throws {
      //This is never called
   }

   func encode(to encoder: Encoder) throws {
      //This is never called
   }
}

背景故事:

我正在将现有应用程序移植到 iOS,并从 C++ 和 ZA50845B36BEDE2626 的混合物转换为 Swift 此应用程序使用 Metal 来做 3D 图形,因此广泛使用向量、矩阵、四元数等数学结构。

在项目开始时,我决定为这些不同类型使用 Apple 的 simd 库。 我给他们一个 typealias 来匹配我们已经拥有的对象来帮助移植,以及扩展来添加各种没有内置的功能。这一切都很好。

我现在需要能够将这些类型与 JSON 相互转换,但我遇到了问题。 simd 结构已经支持 Codable,但它们使用的格式与我需要支持的格式不同。 由于 simd 使用结构,我不能从中派生并提供我自己的init(from)encode定义。 似乎我也不能将自己的定义放在扩展中(编译器不会抱怨,但我的版本也不会被调用)。

我目前使用结构包装器(即Vector3Codable)来完成这项工作。 但是,这意味着我的任何具有这些 simd 成员的对象还必须提供自定义的 Codable 实现,而不是使用编译器添加的默认支持。

如果可能的话,我宁愿坚持使用 simd,而不是为所有事情提供我自己的实现。

编辑 - 看起来好像我的简化示例代码确实有效(doh。)。 这是不起作用的实际代码。 显然这个问题比我最初想象的要复杂。

typealias Vector3 = SIMD3<Float>

extension Vector3 {
    enum CodingKeys: String, CodingKey {
        case x
        case y
        case z
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let x = try container.decodeIfPresent(Float.self, forKey: .x) ?? 0.0
        let y = try container.decodeIfPresent(Float.self, forKey: .y) ?? 0.0
        let z = try container.decodeIfPresent(Float.self, forKey: .z) ?? 0.0

        self.init(x, y, z)
    }
}

问题更新后编辑

确实,您不能在扩展中覆盖 SIMD3 的可编码行为。 但是,您可以尝试将 SIMD 包装在简单的转发器结构中,然后允许您控制它们的编码:

struct Vector3 { // instead of the typealias
    private var simd: SIMD3<Float>
    var x: Float { return simd.x }
    var y: Float { return simd.y }
    var z: Float { return simd.z }
    // add forwarders for other SIMD methods/properties you use

    init(_ x: Float, _ y: Float, _ z: Float) {
        simd = SIMD3(x, y, z)
    }
}

extension Vector3: Codable {
    enum CodingKeys: String, CodingKey {
        case x, y, z
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let x = try container.decodeIfPresent(Float.self, forKey: .x) ?? 0.0
        let y = try container.decodeIfPresent(Float.self, forKey: .y) ?? 0.0
        let z = try container.decodeIfPresent(Float.self, forKey: .z) ?? 0.0

        self.init(x, y, z)
    }
}

(为简洁起见,我只包括了 getter 和Decodable


这按预期工作:

struct Obj1 : Codable {
   var values:[Int]
}

extension Obj1 {
    enum CodingKeys: String, CodingKey {
        case x, y, z
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let x = try container.decode(Int.self, forKey: .x)
        let y = try container.decode(Int.self, forKey: .y)
        let z = try container.decode(Int.self, forKey: .z)

        self.values = [x, y, z]
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(values[0], forKey: .x)
        try container.encode(values[1], forKey: .y)
        try container.encode(values[2], forKey: .z)
    }
}

let obj = Obj1(values: [7, 42, 17])

do {
    let data = try JSONEncoder().encode(obj)
    print(String(bytes: data, encoding: .utf8)!) // "{"x":7,"y":42,"z":17}"

    let o = try JSONDecoder().decode(Obj1.self, from: data)
    print(o.values) // [7, 42, 17]
} catch {
    print(error)
}

当您的实现从未被调用时,请显示编码/解码您的obj1实例的代码。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM