由于复杂的原因,我发现自己在反对Codable
:在解码json对象时,我希望将子对象保存在键extra
下,“就像json一样”,存储在[String: Any]
字典中,但[String: Any]
当然不是Decodable
。有办法这样做吗?
第一个答案当然是“不要”。对此,我的响应是“我需要去做”:我需要让两个Codable
传递数据,其中第一个解码一个异构对象的列表(每个对象都有一个键name
),而第二次传递使用一个按这些name
值键键的字典,并且是正确的类型安全的。第一次传递不能是类型安全的,因为它是在异构列表上操作的,但是它需要保存第二次传递将处理的所有数据。谢天谢地,所有的异构数据都隐藏在这个extra
键下,但我仍然不知道该如何做。
(关于_en_coding,很可能会有一个类似的问题,所以如果你碰巧有洞察力,那就随便提吧。)
发布于 2019-01-08 23:31:55
这个Swift论坛帖子似乎提供了一个解决方案。它是这样开始的:
public enum JSON : Codable {
case null
case number(NSNumber)
case string(String)
case array([JSON])
case dictionary([String : JSON])
// ...
其思想是使显式的JSON
枚举类型,即Codable
,直接编码json结构(case string(String); case array([JSON]);
等)。未分析的子对象被强类型为JSON
,可以通过Codable
进行编码/解码,并且(作为奖励)还可以使用"just Swift“(打开情况并做一些事情)进行必要的分析。
发布于 2019-01-08 10:45:15
您可以创建一个自定义字典Decodable
,它对Dictionary
中的值进行解码和存储,然后对要存储原始字典的键使用此类型。
这是可解码的:
struct DictionaryDecodable: Decodable {
let dictionary : [String: Any]
private struct Key : CodingKey {
var stringValue: String
init?(stringValue: String) {
self.stringValue = stringValue
}
var intValue: Int?
init?(intValue: Int) {
return nil
}
}
init(from decoder: Decoder) throws {
let con = try decoder.container(keyedBy: Key.self)
var dict = [String: Any]()
for key in con.allKeys {
if let value = try? con.decode(String.self, forKey:key) {
dict[key.stringValue] = value
} else if let value = try? con.decode(Int.self, forKey:key) {
dict[key.stringValue] = value
} else if let value = try? con.decode(Double.self, forKey:key) {
dict[key.stringValue] = value
} else if let value = try? con.decode(Bool.self, forKey:key) {
dict[key.stringValue] = value
} else if let data = try? con.decode(DictionaryDecodable.self, forKey:key) {
dict[key.stringValue] = data.dictionary
}
}
self.dictionary = dict
}
}
现在,您可以使用这个结构来解码字典,如下所示:
struct Test: Decodable {
let name: String
let data: [String: Any]
enum Keys: String, CodingKey {
case name
case data
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Keys.self)
name = try container.decode(String.self, forKey: .name)
data = try container.decode(DictionaryDecodable.self, forKey: .data).dictionary // Here use DictionaryDecodable
}
}
让我们来测试:
let data = """
{
"name": "name",
"data": {
"string": "rt",
"bool": true,
"float": 1.12,
"int": 1,
"dict": {
"test": "String"
}
}
}
"""
let s = try JSONDecoder().decode(Test.self, from: data.data(using: .utf8)!)
print(s.name)
print(s.data)
这是输出:
name
["bool": true, "string": "rt", "int": 1, "float": 1.12, "dict": ["test": "String"]]
发布于 2019-01-08 10:21:17
我得到的最接近的是以下操场代码:
struct Foo: Codable {
let bar: [String: Any]
enum CodingKeys: String, CodingKey {
case bar
}
init(bar: [String: Any]) {
self.bar = bar
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
let barString = try values.decode(String.self, forKey: .bar)
let barData = Data(barString.utf8)
let json: [String: Any] = try JSONSerialization.jsonObject(with: barData, options: .allowFragments) as! [String: Any]
bar = json
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
let barData = try JSONSerialization.data(withJSONObject: bar, options: .sortedKeys)
let barString = String(decoding: barData, as: UTF8.self)
try container.encode(barString, forKey: .bar)
}
}
let foo = Foo(bar: ["someInt": 1])
let fooData = try! JSONEncoder().encode(foo)
print(String(decoding: fooData, as: UTF8.self))
print(String(decoding: fooData, as: UTF8.self).replacingOccurrences(of: "\\\"", with: "\""))
let decodedFoo = try! JSONDecoder().decode(Foo.self, from: fooData)
print(decodedFoo)
上述指纹:
{"bar":"{\"someInt\":1}"}
{"bar":"{"someInt":1}"}
Foo(bar: ["someInt": 1])
它还不完美,因为它在生成的JSON字符串中转义了"
,因此可能会破坏其中的一些内容。额外的字符串替换传递可能不是什么令人愉快的事情。
https://stackoverflow.com/questions/54087588
复制相似问题