我有一些JSON,我想用JSONDecoder
来解码.问题是,当从服务器发送时,其中一个属性的名称是动态的.
这样地:
{
"someRandomName": [ [1,2,3], [4,5,6] ],
"staticName": 12345
}
当someRandomName
在构建时未知时,我该如何解码?我一直在网上搜寻答案,但仍然没有找到快乐.我真的搞不懂这Decodable
CodingKey
的工作原理.有些例子有几十行,但这似乎不正确!
EDIT我应该指出,密钥在运行时是已知的,所以也许我可以在解码对象时传递它?
有没有办法连接到协议方法或属性中的一个来启用这种解码?我不介意我是否必须为这个对象编写一个定制的解码器:所有其他JSON都是标准的.
EDIT
好吧,我的理解让我走了这么远:
struct Pair: Decodable {
var pair: [[Double]]
var last: Int
private struct CodingKeys: CodingKey {
var stringValue: String
init?(stringValue: String) {
self.stringValue = stringValue
}
// Use for integer-keyed dictionary
var intValue: Int?
init?(intValue: Int) {
// We are not using this, thus just return nil
return nil
}
}
init(from decoder: Decoder) throws {
// just to stop the compiler moaning
pair = [[]]
last = 0
let container = try decoder.container(keyedBy: CodingKeys.self)
// how do I generate the key for the correspond "pair" property here?
for key in container.allKeys {
last = try container.decode(Int.self, forKey: CodingKeys(stringValue: "last")!)
pair = try container.decode([[Double]].self, forKey: CodingKeys(stringValue: key.stringValue)!)
}
}
}
init() {
let jsonString = """
{
"last": 123456,
"XBTUSD": [ [1.0, 2.0, 3.0], [4.0, 5.0, 6.0] ]
}
"""
let jsonData = Data(jsonString.utf8)
// this gives: "Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.typeMismatch(Swift.Array<Any>, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "last", intValue: nil)], debugDescription: "Expected to decode Array<Any> but found a number instead.", underlyingError: nil))"
let decodedResult = try! JSONDecoder().decode(Pair.self, from: jsonData)
dump(decodedResult)
}
所以我现在明白了,CodingKey
一致性是为序列化数据生成密钥,而不是Swift struct (现在我想这是非常合理的).
那么,我现在如何动态生成pair
的 case ,而不是像这样硬编码呢?我知道这与我需要实现的init(from decoder: Decoder)
有关,但就我的一生而言,我无法弄清楚它是如何工作的.请帮忙!
EDIT 2
好了,我现在离你很近了.解码似乎与此有关:
struct Pair: Decodable {
var pair: [[Double]]
var last: Int
private enum CodingKeys : String, CodingKey {
case last
}
private struct DynamicCodingKeys: CodingKey {
var stringValue: String
init?(stringValue: String) {
self.stringValue = stringValue
}
// Use for integer-keyed dictionary
var intValue: Int?
init?(intValue: Int) {
// We are not using this, thus just return nil
return nil
}
}
init(from decoder: Decoder) throws {
// just to stop the compiler moaning
pair = [[]]
last = 0
let container1 = try decoder.container(keyedBy: CodingKeys.self)
last = try container1.decode(Int.self, forKey: .last)
let container2 = try decoder.container(keyedBy: DynamicCodingKeys.self)
for key in container2.allKeys {
pair = try container2.decode([[Double]].self, forKey: DynamicCodingKeys(stringValue: key.stringValue)!)
}
}
}
这段代码似乎完成了它的工作:判断函数本身的last
和pair
属性,结果看起来不错;但我在try 解码时出错了:
init() {
let jsonString = """
{
"last": 123456,
"XBTUSD": [ [1.0, 2.0, 3.0], [4.0, 5.0, 6.0] ]
}
"""
let jsonData = Data(jsonString.utf8)
// Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.typeMismatch(Swift.Array<Any>, Swift.DecodingError.Context(codingPath: [DynamicCodingKeys(stringValue: "last", intValue: nil)], debugDescription: "Expected to decode Array<Any> but found a number instead."
let decodedResult = try! JSONDecoder().decode(Pair.self, from: jsonData)
dump(decodedResult)
}
我离得很近,我能尝到...