【问题标题】:Swift Codable: Cannot decode dictionary of type [String: Any] or [String: Decodable]Swift Codable:无法解码类型为 [String: Any] 或 [String: Decodable] 的字典
【发布时间】:2020-10-08 08:49:02
【问题描述】:

在我的自定义初始化程序中,我想从 JSON 解码字典,然后将其值分配给类中的属性。令我惊讶的是,编译器不允许我解码字典,我收到错误:

Value of protocol type 'Any' cannot conform to 'Decodable'; only struct/enum/class types can conform to protocols

如果我尝试解码 [String: Decodable] 类型的字典,错误消息会显示:

Value of protocol type 'Decodable' cannot conform to 'Decodable'; only struct/enum/class types can conform to protocols

我的初始化程序如下所示:

public let total: Int

required init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    ...
    if let dict = try container.decodeIfPresent([String: Any].self, forKey: .tracks),
           let value = dict["total"] as? Int { // Error is displayed at this line
        total = value
    } else {
        total = 0
    }
    ...
}

当我寻找答案时,我找到了this answer,根据它上面的代码应该不会造成任何问题。

【问题讨论】:

  • “我想从 JSON 解码一个字典,然后将它的值分配给类中的属性”您实际上是在寻找 nestedContainer 吗?
  • "我想从 JSON 解码字典,然后将其值分配给类中的属性" 您是否真的在寻找 nestedContainer?另外“根据它,上面的代码不应该引起任何问题。”您是否像用户一样添加了扩展程序?

标签: ios swift codable


【解决方案1】:

您正在寻找的是nestedContainer。它可以帮助您“跳过” JSON 中的层次结构级别。即:假设在您的代码中,它都在同一级别(一个结构),但在 JSON 中,它不是,有一个子字典。

出于测试目的,您的 JSON 可能如下所示:

{
    "title": "normal",
    "tracks": {
                   "name": "myName",
                   "total": 3
              }
}

如果我们想要在我们的模型中:

struct TestStruct: Codable {
    let title: String
    let name: String
    let total: Int
}

我们需要使用nestedContainer(keyedBy: forKey:):

extension TestStruct {
    enum TopLevelKeys: String, CodingKey {
        case title
        case tracks
    }
    
    enum NestedLevelCodingKeys: String, CodingKey {
        case name
        case total
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: TopLevelKeys.self)
        self.title = try container.decode(String.self, forKey: .title)
        let subcontainer = try container.nestedContainer(keyedBy: NestedLevelCodingKeys.self, forKey: TopLevelKeys.tracks)
        self.name = try subcontainer.decode(String.self, forKey: .name)
        self.total = try subcontainer.decode(Int.self, forKey: .total) //You can use here a `decodeIfPresent()` if needed, use default values, etc.

    }
}

在您的示例中,您使用 decodeIfPresent() 作为子字典。目前尚不清楚它是出于测试目的,还是有时不存在子词典。 如果是这种情况,您可以拥有这样的 JSON:

{
    "title": "normal"
} 

然后,在调用nestedContainer(keyedBy: forKey:) 之前,使用if container.contains(TopLevelKeys.tracks) {} 并在else 情况下根据需要设置默认值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-04-11
    • 2020-02-04
    • 1970-01-01
    • 2018-08-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多