Swift Codable:无法解码 [String: Any] 或 [String: Decodable] 类型的字典
Swift Codable: Cannot decode dictionary of type [String: Any] or [String: Decodable]
在我的自定义初始化程序中,我想从 JSON 解码字典,然后将其值分配给 class 中的属性。令我惊讶的是,编译器不允许我解码字典,我收到错误:
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
}
...
}
当我寻找答案时,我找到了 ,根据它上面的代码应该不会造成任何问题。
您要找的是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
情况下设置默认值。
在我的自定义初始化程序中,我想从 JSON 解码字典,然后将其值分配给 class 中的属性。令我惊讶的是,编译器不允许我解码字典,我收到错误:
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
}
...
}
当我寻找答案时,我找到了
您要找的是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
情况下设置默认值。