自分で定義したenum型が含まれているstructをJSONからデコードする方法で少し悩んだので、解決方法を共有します。
Type 'Character' does not conform to protocol 'Decodable'
次のように、Character
というstructの中にStatus
というenum型のプロパティを定義した場合に、Type 'Character' does not conform to protocol 'Decodable'
というエラーが出てビルドができない。なんでだ!と悩みました。
case normal
case sleep
case confuse
}
struct Character: Decodable {
let name: String
let attack: Int
let defence: Int
let status: Status
}
自分で定義したenum型もDecodableにする
上述のビルドエラーを解消するには、Status
にDecodable
を継承させます。
enum Status: Decodable {
case normal
case sleep
case confuse
}
struct Character: Decodable {
let name: String
let attack: Int
let defence: Int
let status: Status
}
Decodableとして定義したstructに含まれるプロパティもまた、Decodableを実装している必要があるというルールによるものです。
デコード時にエラー
Status
にDecodable
を継承させることでビルドエラーは消えますが、実際にJSONDecoder
などを使ってデコードしてみると、うまくいきません。
{
"name": "jimaru",
"attack": 2,
"defence": 3,
"status": "sleep"
}
""".data(using: .utf8)!
let decoder = JSONDecoder()
let character = try! decoder.decode(Character.self, from: json)
// 次のエラーが発生:
// 'try!' expression unexpectedly raised an error:
// Swift.DecodingError.typeMismatch(Swift.Dictionary
// Swift.DecodingError.Context(
// codingPath: [CodingKeys(stringValue: "status", intValue: nil)],
// debugDescription: "Expected to decode Dictionary
// underlyingError: nil))
これはJSON内のstatusのデータが何であったらStatus.normal
としてデコードするのか、Status.sleep
としてデコードするのか、という情報が不足しているためです。
Status
のrawValue
を文字列で設定すれば、このrawValue
の値からデコードしてくれるようになります。
case normal = "normal"
case sleep = "sleep"
case confuse = "confuse"
}
rawValueについてのドキュメントはThe Swift Programming Language > Enumerations > Raw Valuesです。
あとがき
今回の内容は、Apple公式ドキュメントのEncoding and Decoding Custom Typesを発見したことで解決しました。
Decodableプロトコルのページからもリンクが貼ってあったので、真っ先に辿り着くべきページでしたね。。ググって出てきた見当違いの記事を読んでしまったことで頭を抱え、無駄な時間を使ってしまいやした。
この記事により頭を抱える人が減る手助けになりますように。。