JSON 中的 Codable 解码值将 snake case 转换为 camel

Codable decoding values in JSON converting snake case to camel

当使用 Swift Codable 时,您可以指定 keyDecodingStrategy 将 snake_case 转换为驼峰式。这对字典中的键很有用,但是有没有任何解决方案可以以类似的方式对字典中的值进行解码?

我有一个枚举,在一个地方用作键,在另一个地方用作值:

enum Foo: String, Codable, CodingKey, CaseIterable {
   case bar
   case bazQux // baz_qux in JSON, say
}

然后像这样用作密钥:

struct MyStruct: Codable {
    enum Keys: String, CodingKey {
        case myKey
    }

    let myProperty: [Bool]

    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: Foo.self)

        myProperty = Foo.allCases.map {
            try container.decode(Bool.self, forKey: [=12=])
        }
    }
}

但它也被用作这样的值:

struct Buz: Decodable {
    enum CodingKeys: String, CodingKey {
        case foo
    }

    // Note this is called using a decoder with keyDecodingStrategy = .convertFromSnakeCase
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let foo = try container.decode(Foo.self, forKey: .foo)
    }
}

例如 JSON 可能包含这样一行:

"foo": "baz_qux"

仅当我将 bazQux 案例的原始值设置为 baz_qux 时,值解码才有效。但这会破坏密钥解码。为了避免这个问题,必须为同一件事制作两个单独的枚举也很烦人。

这样我就可以初始化对应值的正确模型了

我也想避免使用“hacky”解决方案……是否有适合 Codable 的相当优雅的解决方案?

最终在 Foo 上添加了自定义解码初始化程序:

 public init(from decoder: Decoder) throws {
    let rawValue = try decoder.singleValueContainer().decode(String.self)
    if let foo = Self.init(rawValue: rawValue.snakeToCamelCase) {
      self = foo
    } else {
      throw CodingError.unknownValue
    }
  }

snakeToCamelCase是我在本地添加的String上的简单扩展方法。如果您需要一些错误处理,还需要将 enum CodingError: Error 添加到 Foo

这并不理想,但至少不太复杂。我宁愿依赖内置的大小写转换方法,但我在这里看不到这样做的方法。