I’m trying to implement a custom init(from decoder: Decoder) throws {}
for the Decodable protocol, but I get an error:
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)
What do I need to do to make the code work correctly ?
2
Answers
I don´t think this can be done with
JsonDecoder
alone. Essentially the value ofgotoAction
is of typeString
and not a valid Json.That´s the meaning of the error message you get.
You would need to interpret and convert this string to the enums yourself.
I´ve implemented a possible solution with the information you provided for
GotoAction
enum. Of course this needs more work like checking for the correct keys and throwing errors appropriately.You need to extract parts from the string corresponding to the
goto_action
key. This is no longer parsing JSON. You need to write your own logic for parsing things likewebsite_link/foobarbaz
andopen_sheet/second_banner/foo
, and turn that into a value ofGotoAction
.The
init(from:)
forGotoAction
can be written like this:Note that
OpenSheet
doesn’t need to beDecodable
anymore. It only occurs as a substring of a larger JSON string, which you extract by manually manipulating the JSON string.