複数の型を持つJSONをemumでDecodeする
複数の型を持つ JSON を enum を使って Decode する方法です。 とある API を叩くと JSON が返ってきますが、その中身がポケモンなのかトレーナーなのかジムリーダーなのかわかりません。 以下のコードのように enum の Character の中に Pokemon, GymLeader, Trainer を定義することで、どのような JSON が来ても Decode できるようになります。
Main.swift
import Foundation
struct Pokemon: Codable {
let id: Int
let name: String
}
struct GymLeader: Codable {
let name: String
let badge: String
let pokemons: [Pokemon]
}
struct Trainer: Codable {
let name: String
let pokemons: [Pokemon]
}
enum Character: Codable {
case pokemon(Pokemon)
case gymLeader(GymLeader)
case trainer(Trainer)
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let pokemon = try? container.decode(Pokemon.self) {
self = .pokemon(pokemon)
return
}
if let gymLeader = try? container.decode(GymLeader.self) {
self = .gymLeader(gymLeader)
return
}
if let trainer = try? container.decode(Trainer.self) {
self = .trainer(trainer)
return
}
throw CharacterError.failToDecode
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case let .pokemon(pokemon):
try container.encode(pokemon)
case let .gymLeader(gymLeader):
try container.encode(gymLeader)
case let .trainer(trainer):
try container.encode(trainer)
}
}
}
enum CharacterError: LocalizedError {
case failToDecode
var errorDescription: String? {
switch self {
case .failToDecode:
return "Fail to Decode"
}
}
}
let pokemonData = """
{
"id": 143,
"name": "Snorlax"
}
""".data(using: .utf8)!
let pokemon = try! JSONDecoder().decode(Character.self, from: pokemonData)
print(pokemon)
let gymLeaderData = """
{
"name": "Brock",
"badge": "Gray Badge",
"pokemons": [
{
"id": 95,
"name": "Onix"
}
]
}
""".data(using: .utf8)!
let gymLeader = try! JSONDecoder().decode(Character.self, from: gymLeaderData)
print(gymLeader)
let trainerData = """
{
"name": "Super Nerd",
"pokemons": [
{
"id": 81,
"name": "Magnemite"
}
]
}
""".data(using: .utf8)!
let trainer = try! JSONDecoder().decode(Character.self, from: trainerData)
print(trainer)
let errorData = """
{
"name": "Error"
}
""".data(using: .utf8)!
do {
_ = try JSONDecoder().decode(Character.self, from: errorData)
} catch {
print(error.localizedDescription)
}
