可解码 属性 包装器的自动可解码合成
Automatic decodable synthesis for decodable property wrappers
假设我有可解码的 属性 包装器:
@propertyWrapper
struct OptionalDecodable<Value: Decodable>: Decodable {
var wrappedValue: Value?
}
编译器会为以下内容合成 init
struct Model: Decodable {
@OptionalDecodable private(set) var string: String?
}
为了测试这是否有效,我只是尝试解码空 JSON(即“{}”)
但是,string
属性 不被视为可选,即当没有 string
密钥时,我会收到找不到密钥的错误消息。
有解决办法吗?
我不确定这是否是最好的方法,但问题是 属性 包装器的 wrappedValue
类型必须与 属性 的类型匹配,并且String
不同于 String?
。
克服这个问题的一种方法是使 属性 包装器通用,但以允许您从 String
或 Int
初始化类型的方式进行约束:
protocol ExpressibleByString {
init(fromString: String)
}
extension String: ExpressibleByString {
init(fromString: String) { self = fromString }
}
extension Optional: ExpressibleByString where Wrapped == String {
init(fromString: String) { self = fromString }
}
@propertyWrapper
struct IntOrString<V: ExpressibleByString & Decodable>: Decodable {
var wrappedValue: V
}
extension IntOrString {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
do {
let int = try container.decode(Int.self)
wrappedValue = .init(fromString: int.description)
} catch DecodingError.typeMismatch {
wrappedValue = try .init(fromString: container.decode(String.self))
}
}
}
extension KeyedDecodingContainer {
func decode<V: ExpressibleByNilLiteral>(_ t: IntOrString<V>.Type, forKey key: K) throws -> IntOrString<V> {
if let v = try decodeIfPresent(t, forKey: key) {
return v
}
return IntOrString(wrappedValue: nil)
}
}
然后你可以在可选和非可选上使用它String
:
struct Foo: Decodable {
@IntOrString
var p1: String?
@IntOrString
var p2: String
}
假设我有可解码的 属性 包装器:
@propertyWrapper
struct OptionalDecodable<Value: Decodable>: Decodable {
var wrappedValue: Value?
}
编译器会为以下内容合成 init
struct Model: Decodable {
@OptionalDecodable private(set) var string: String?
}
为了测试这是否有效,我只是尝试解码空 JSON(即“{}”)
但是,string
属性 不被视为可选,即当没有 string
密钥时,我会收到找不到密钥的错误消息。
有解决办法吗?
我不确定这是否是最好的方法,但问题是 属性 包装器的 wrappedValue
类型必须与 属性 的类型匹配,并且String
不同于 String?
。
克服这个问题的一种方法是使 属性 包装器通用,但以允许您从 String
或 Int
初始化类型的方式进行约束:
protocol ExpressibleByString {
init(fromString: String)
}
extension String: ExpressibleByString {
init(fromString: String) { self = fromString }
}
extension Optional: ExpressibleByString where Wrapped == String {
init(fromString: String) { self = fromString }
}
@propertyWrapper
struct IntOrString<V: ExpressibleByString & Decodable>: Decodable {
var wrappedValue: V
}
extension IntOrString {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
do {
let int = try container.decode(Int.self)
wrappedValue = .init(fromString: int.description)
} catch DecodingError.typeMismatch {
wrappedValue = try .init(fromString: container.decode(String.self))
}
}
}
extension KeyedDecodingContainer {
func decode<V: ExpressibleByNilLiteral>(_ t: IntOrString<V>.Type, forKey key: K) throws -> IntOrString<V> {
if let v = try decodeIfPresent(t, forKey: key) {
return v
}
return IntOrString(wrappedValue: nil)
}
}
然后你可以在可选和非可选上使用它String
:
struct Foo: Decodable {
@IntOrString
var p1: String?
@IntOrString
var p2: String
}