[英]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.