我遇到的情况是服务器向我发送一个模型,在该模型中我知道某些键的类型和名称,而对其他键则不了解。但是,用户可以编辑自己的其他键值对。
示例:
{ "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)
}
}
但是我收到以下编译时错误:
协议类型“任何”都不能符合“可解码”,因为只有具体类型才能符合协议
似乎也无法使用JSONDecoder
给我们Data
引用原始NSJSONSerialization
。因此,我想可以反过来做,首先使用较旧的技术初始化dict,然后使用JSONDecoder
初始化模型,并用传递Data的东西替换init,但这感觉很不对劲,因为我们将有效地反序列化两次:/
答案 0 :(得分:0)
我不同意您将其解析为[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")