【问题标题】:Swift Decodable - how to avoid generic type?Swift Decodable - 如何避免泛型类型?
【发布时间】:2020-08-19 12:08:55
【问题描述】:

我正在从我的 JSON REST API 中检索一个复杂的嵌套对象。

DocumentDraft
 - uuid: String
 - schema: Schema // Very complicated object with many variations
 - url: String
 - values: [Value]
 - successors: [String]
 - predecessors: [String]

Value
 - key: String
 - val: String? OR [String]?   // <-- This is the problem

我想解决这个问题的正确方法是引入一个泛型类型。

struct Value<V: Decodable>: Decodable {
  let key: String
  let val: V?
}

...但即便如此,values 可能是一个混合数组,所以我看不出声明 V 是什么会有帮助。

但是,当然,泛型类型会一直传播到层次结构中,传播到 DocumentDraft 对象、发布者、我的 API 调用等。污染了整个链,否则非常干净和可读的调用和对象.我只想在Value 的级别上处理这个问题,并让 JSONDecoder 以某种方式简单地返回两者之一。

是否有另一种方法可以在不更改整个父对象的情况下将可选的val 的两种可能性处理为String[String]

【问题讨论】:

  • 请发送您的 JSON 示例
  • 您可以将enum 与关联值一起使用

标签: swift generics codable jsonencoder encodable


【解决方案1】:

您可以只使用[String] 类型并手动实现Decodable 协议的init(from:) 函数,如下所示:

struct Value: Decodable {
    let key: String
    let val: [String]?
    
    enum CodingKeys: String, CodingKey {
        case key, val
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        
        key = try container.decode(String.self, forKey: .key)
        do {
            if let string = try container.decodeIfPresent(String.self, forKey: .val) {
                val = [string]
            } else {
                val = nil
            }
        } catch DecodingError.typeMismatch {
            val = try container.decodeIfPresent([String].self, forKey: .val)
        }
    }
}

当解码到String 值成功时,创建一个只有一个元素的字符串数组。当解码为String值失败时,尝试解码为[String]

【讨论】:

  • 这是一个很好的方法,但正如所写的那样,当它应该抛出错误时,它会忽略格式错误的 JSON。您可以通过使用 do-catch 块而不是 if-let 和 try decodeIfPresent 而不是 try? decode 来改进这一点。
  • @RobNapier 你是对的。我根据您的建议更新了我的答案。
猜你喜欢
  • 1970-01-01
  • 2016-09-14
  • 2021-06-11
  • 1970-01-01
  • 1970-01-01
  • 2015-12-09
  • 2020-12-15
  • 2021-12-08
  • 1970-01-01
相关资源
最近更新 更多