I am trying to decode an Enum which consists of associated values. I am trying the following but it keeps throwing exceptions.
let jsonString = """
{
"route": "petDetails"
}
"""
let jsonData = jsonString.data(using: .utf8)
struct Post: Decodable {
let route: Route
}
enum Route: Decodable, Equatable {
case petDetails(String)
init?(rawValue: String) {
switch rawValue {
case "petDetails":
self = .petDetails("")
default:
return nil
}
}
private enum CodingKeys: String, CodingKey {
case petDetails
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let value = try? container.decode(String.self, forKey: .petDetails) {
self = .petDetails(value)
} else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: container.codingPath, debugDescription: "Data doesn't match"))
}
}
}
try! JSONDecoder().decode(Post.self, from: jsonData!)
I get the following error:
Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "route", intValue: nil)], debugDescription: "Expected to decode Dictionary<String, Any> but found a string/data instead.", underlyingError: nil))
Any ideas what I am missing?
Suppose we have an enum with an associated value:
enum Route: Codable, Equatable {
case petDetails(name: String)
case petListing(count: Int)
...
The JSON that represents each Route
should contain two values:
petDetails
vs petListing
), andOur coding keys will be
private enum CodingKeys: String, CodingKey {
case type
case associatedValue
}
and the decoding function will be
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decode(String.self, forKey: .type)
switch type {
case "petDetails":
// The associated value is a String
let name = try container.decode(String.self, forKey: .associatedValue)
self = .petDetails(name: name)
case "petListing":
// The associated value is an Int
let count = try container.decode(Int.self, forKey: .associatedValue)
self = .petListing(count: count)
default:
throw DecodingError.dataCorruptedError(forKey: .type, in: container, debugDescription: "Invalid type")
}
}
We first decode the type, and that tells us what kind of data the associated value will be.
The JSON for a Post
would look like
{
"route": {
"type": "petDetails",
"associatedValue": "Rex"
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.