问题描述
在我的自定义初始化程序中,我想从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,根据它,上面的代码应该不会引起任何问题。
解决方法
您要寻找的是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
情况下根据需要设置默认值。