是否有一种方法可以覆盖特定类型的Decodable实现,以使该类型成为Codable
结构的一部分?
具体来说,我有这个结构:
struct Activity: Codable {
let id: Int
let name: String
<...>
let distance: Measurement<UnitLength>
}
我想要一种提供像这样的解码初始化器的方法:
extension Measurement where UnitType == UnitLength {
public init(from decoder: Decoder) throws {
self = Measurement(value: try decoder.singleValueContainer().decode(Double.self), unit: UnitLength.meters)
}
}
这样我就可以在解码时将Double
的值转换为Measurement<UnitLength>
,而而无需必须为每个init(from decoder: Decoder)
提供自定义struct
其中有一个Measurement
。
init
可以很好地编译,但是似乎没有从标准Codable
解码过程中调用。
我的解决方法是Activity
结构的初始化:
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
<...>
distance = Measurement(value: try values.decode(Double.self, forKey: .distance), unit: UnitLength.meters)
}
但是我宁愿在Measurement
上实现一次,而不是为每种可能使用它的类型实现。
答案 0 :(得分:1)
一种可能满足您需要的解决方案是定义自己的测量结构,该结构假定以米为单位的长度测量。
struct MyMeasurement {
let length: Measurement<UnitLength>
}
extension MyMeasurement: Codable {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
length = Measurement(value: try container.decode(Double.self), unit: UnitLength.meters)
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(length.value)
}
}
然后更新您的Activity
结构以使用MyMeasurement
:
struct Activity: Codable {
let id: Int
let name: String
<...>
let distance: MyMeasurement
}
然后您可以正常解码:
let decoder = JSONDecoder()
do {
let activity = try decoder.decode(Activity.self, from: someJSONData)
print(activity.distance.length)
} catch {
print(error)
}
您可能想要一个MyMeasurement
更好的名称,暗示它是一个以米为单位的长度。
您可以在任何具有米长度属性的结构中重用MyMeasurement
,而无需为该结构编写自定义init(from:)
。