Swift Codable:用未知键解码字典

Swift Codable: decode dictionary with unknown keys

当您知道 JSON 数据的关键格式时,

Codable 非常有用。但是,如果您不知道密钥怎么办?我目前正面临这个问题。

通常我希望 JSON 数据像这样返回:

{
"id": "<123>",
"data": [
    {
        "id": "<id1>",
        "event": "<event_type>",
        "date": "<date>"
    },
    {
        "id": "<id2>",
        "event": "<event_type>",
        "date": "<date>"
    },
]
}

但这就是我要解码的内容:

{
"id": "123",
"data": [
    { "<id1>": { "<event>": "<date>" } },
    { "<id2>": { "<event>": "<date>" } },
]
}

问题是: 我如何使用 Codable 解码 JSON 密钥是唯一的?我觉得我错过了一些明显的东西。

这就是我希望这样做的,这样我就可以使用 Codable:

struct SampleModel: Codable {
    let id: String
    let data: [[String: [String: Any]]]

    // MARK: - Decoding

    enum CodingKeys: String, CodingKey {
        case id = "id"
        case data = "data"
    }

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

        id = try container.decode(String.self, forKey: .id)
        // This throws an error: Ambiguous reference to member 'decode(_:forKey:)'
        data = try container.decode([[String: [String: Any]]].self, forKey: .data)
    }
}

这会引发错误:Ambiguous reference to member 'decode(_:forKey:)'

对于你完全改变的问题,解决方案非常相似。您的结构只是在数组上方添加了一个额外的层。不需要任何自定义解码,甚至不需要任何 CodingKeys。

请注意,您不能在 Codable 中使用 Any

let json="""
{
"id": "123",
"data": [
    { "<id1>": { "<event>": "2019-05-21T16:15:34-0400" } },
    { "<id2>": { "<event>": "2019-07-01T12:15:34-0400" } },
]
}
"""
struct SampleModel: Codable {
    let id: String
    let data: [[String: [String: Date]]]
}

var decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
do {
    let res = try decoder.decode(SampleModel.self, from: json.data(using: .utf8)!)
    print(res)
} catch {
    print(error)
}

原始问题的原始答案。

由于您有一个嵌套字典数组,其中 none 个字典键是固定的,并且由于没有其他字段,您可以将其解码为普通数组。

这是一个例子:

let json="""
[
    { "<id1>": { "<event>": "2019-07-01T12:15:34-0400" } },
    { "<id2>": { "<event>": "2019-05-21T17:15:34-0400" } },
]
"""
var decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
do {
    let res = try decoder.decode([[String: [String: Date]]].self, from: json.data(using: .utf8)!)
    print(res)
} catch {
    print(error)
}