我正在尝试为 Decodable 协议实现自定义
init(from decoder: Decoder) throws {}
,但出现错误:
DecodingError.typeMismatch(Swift.Dictionary<Swift.String, Foundation.JSONValue>)
Expected to decode Dictionary<String, JSONValue> but found a string instead.
struct Model: Decodable {
let title: String
let gotoAction: GotoAction // String work fine
}
enum GotoAction: Decodable {
case noAction
case websiteLink(String)
case sheet(OpenSheet)
private enum CodingKeys: String, CodingKey {
case noAction
case websiteLink
case sheet
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let link = try container.decodeIfPresent(String.self, forKey: .websiteLink) {
self = .websiteLink(link)
}
if let sheet = try container.decodeIfPresent(OpenSheet.self, forKey: .sheet) {
self = .sheet(sheet)
}
// let noAction = try container.decodeIfPresent(String.self, forKey: .noAction)
// self = noAction
throw DecodingError.dataCorruptedError(forKey: .websiteLink, in: container, debugDescription: "No match")
}
}
enum OpenSheet: Decodable {
case firstBanner
case secondBanner(String)
}
let json = """
{
"title": "Hello",
"goto_action": "no_action"
}
"""
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let result = try decoder.decode(Model.self, from: json.data(using: .utf8)!)
print(result)
我需要做什么才能使代码正常工作?
我认为仅凭
JsonDecoder
无法完成此任务。本质上 gotoAction
的值是 String
类型,而不是有效的 Json。
这就是您收到的错误消息的含义。
您需要自己解释该字符串并将其转换为枚举。
我已经使用您为
GotoAction
枚举提供的信息实施了一个可能的解决方案。当然,这需要更多的工作,例如检查正确的密钥并适当地抛出错误。
struct Model: Decodable {
let title: String
let gotoAction: GotoAction // String work fine
private enum CodingKeys: String, CodingKey{
case title
case gotoAction
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.title = try container.decode(String.self, forKey: .title)
let enumString = try container.decode(String.self, forKey: .gotoAction)
self.gotoAction = try GotoAction.fromString(enumString)
}
}
enum GotoAction: Decodable {
case noAction
case websiteLink(String)
case sheet(OpenSheet)
static func fromString(_ enumString: String) throws -> GotoAction{
let array = enumString.split(separator: "/")
let key = array.first!
let associatedValue = array.dropFirst().joined(separator: "/")
if key == "website_link"{
return .websiteLink(associatedValue)
}
else if key == "sheet"{
return .sheet(try OpenSheet.fromString(associatedValue))
}
return .noAction
}
}