【问题标题】:Automatic decodable synthesis for decodable property wrappers可解码属性包装器的自动可解码合成
【发布时间】:2021-02-21 12:34:58
【问题描述】:

假设我有可解码的属性包装器:

@propertyWrapper
struct OptionalDecodable<Value: Decodable>: Decodable {
  var wrappedValue: Value?
}

编译器会为以下内容合成 init

struct Model: Decodable {
  @OptionalDecodable private(set) var string: String?
}

为了测试这是否可行,我只是尝试解码空 JSON(即“{}”)

但是,string 属性不被视为可选,即当没有 string 键时,我收到一个错误,即找不到键。

有解决办法吗?

【问题讨论】:

  • 不清楚你在问什么。您可以在问题中添加您尝试解码的 JSON 吗?另外,为什么需要这个属性包装器?可选项已经由 Codable 处理
  • @NewDev 更新了问题,希望对您有所帮助。这个包装器的目的是在后台执行自定义解码。说,我希望从字符串和数字中正确解码字符串。这个想法适用于非可选数据类型。但是当谈到可选时,我就卡住了

标签: swift decodable property-wrapper


【解决方案1】:

我不确定这是否是最好的方法,但问题是属性包装器的wrappedValue 类型必须匹配属性的类型,而StringString? 不同。

解决此问题的一种方法是使属性包装器通用,但以允许您从 StringInt 初始化类型的方式进行约束:

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
}

【讨论】:

  • 好的,我已经为您的代码编写了一个测试:let test = #"{ "p2": "value" }"# let data = test.data(using: .utf8)! do { let object = try JSONDecoder().decode(Foo.self, from: data) print(object) } catch { print(error) } 如您所见,测试 JSON 中缺少属性 p1。输出是keyNotFound(CodingKeys(stringValue: "p1", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"p1\", intValue: nil) (\"p1\").", underlyingError: nil)) 这是我要解决的问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-01-26
  • 2013-04-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多