Swift4 Decodable - 将一半的键解码为字典

标签 swift jsondecoder

我有这样的情况,服务器向我发送了一个模型,我知道一些键的类型和名称,但不知道其他键的类型和名称。但是,用户可以根据自己的意愿编辑其他键值对。

例子:

{ "a": "B",
  "b": 42,
  "__customKey1": "customVal1",
  "__customKey2": [41, 42],
  "__customKey3": {"z":"x"}
}

所以我最终想要的是一个模型,其中包含一些声明的属性和一些填充到 Dictionary<String, Any> 中的值。 ,例如

struct MyStruct {
  var a: String?
  var b: Int?
  var dict: Dictionary<String,Any>
}

我试过类似的方法:

  public struct CodingKeysX: CodingKey {
      public var intValue: Int?
      public var stringValue: String

      public init?(intValue: Int) { self.intValue = intValue; self.stringValue = "\(intValue)" }
      public init?(stringValue: String) { self.stringValue = stringValue }

      static func make(key: String) -> CodingKeysX {
          return CodingKeysX(stringValue: key)!
      }
  }

 init(from decoder: Decoder) throws {

        let co = try! decoder.container(keyedBy: CodingKeysX.self)

        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.a = try container.decode(String?.self, forKey: .a)
        self.b = try container.decode(Int?.self, forKey: .b)
        let allDeclaredKeys = container.allKeys.map({ $0.stringValue })
        self.dict = Dictionary<String, Any>()
        for key in co.allKeys.filter({ !allDeclaredKeys.contains($0.stringValue) }) {
            self.dict[key.stringValue] = try? co.decodeIfPresent(Any.self, forKey: key)
        }
    }

但是我得到以下编译时错误:

Protocol type 'Any' cannot conform to 'Decodable' because only concrete types can conform to protocols

似乎也使用 JSONDecoder我无法引用原始 Data给我们NSJSONSerialization .所以我想,我可以反过来做,我首先使用旧技术初始化字典,然后使用 JSONDecoder 初始化模型。并用传入数据的东西替换 init,但感觉不对,因为我们会有效地反序列化两次:/

最佳答案

我不同意你应该将其解析为 [String: Any]。 JSON 中只有少数几种合法的值类型。这与 Any 相去甚远。

相反,您的起点 IMO 将是 generic JSON decoder .解码为 JSON 枚举。

let value = try JSONDecoder().decode(JSON.self, from: json)
==> Optional(["__customKey1": "customVal1", "b": 42, "a": "B", "__customKey2": [41, 42], "__customKey3": ["z": "x"]])

value["a"]?.stringValue   // Note value is a JSON, not a Dictionary
==> Optional("B")

使用它,为了解决您的特定问题,您可以做这样的事情(非常接近您现有的解码器):

struct MyStruct {
    var a: String?
    var b: Int?
    var dict: [String: JSON] // JSON values, not Any values
}

extension MyStruct: Decodable {

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

        let knownKeys = [JSON.Key("a"), JSON.Key("b")]

        // Unload the known keys. There's no need for these to be Optional unless
        // they really are optional (and nil is different than ""). The point,
        // is you can do any "normal" validation you want here and throw on error.
        self.a = try container.decodeIfPresent(String.self, forKey: JSON.Key("a"))
        self.b = try container.decodeIfPresent(Int.self, forKey: JSON.Key("b"))

        // Unload the rest into your dictionary
        self.dict = [:]
        for key in container.allKeys where !knownKeys.contains(key) {
            self.dict[key.stringValue] = try container.decode(JSON.self, forKey: key)
        }
    }
}

let ms = try JSONDecoder().decode(MyStruct.self, from: json)
=> MyStruct(a: Optional("B"), b: Optional(42), 
            dict: ["__customKey1": "customVal1", 
                   "__customKey2": [41, 42], 
                   "__customKey3": {"z": "x"}])

ms.dict["__customKey1"]?.stringValue  // Optional("customVal1")

关于Swift4 Decodable - 将一半的键解码为字典,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57351147/

相关文章:

ios - 折线图中未显示的数据点

iphone - 使用堆栈的导航 Controller / View Controller

ios - 在 imageView 顶部绘制背景清晰的矩形

ios - Swift - 从 UIViewControllers 转到 'in' 和 'back' 时内存增加

iOS 截图实时相机预览

ios - 使用 Decodable 解析 JSON 返回空模型

ios - 从 API 调用返回的 base64encoded 字符串在数据之后显示为 nil(base64encoded : data) is ran

json - Swift - 将类型动态传递给 JSONDecoder

ios - Swift - 如何使 JSONDecoder 正确解析类层次结构中的日期数组

swift - 在 Swift 4 中解码小写和大写 JSON 键