简体   繁体   中英

Making an enum with associated values conform to decodable?

I am trying to make my enumeration that has different associated values comply to the Decodable protocol, but am having trouble with the associated values part.

enum SportType: Decodable {
    case team(String, String) //Two team names
    case individual([String]) //List of player names
}

I have successfully gotten an enum to conform to Decodable without associated values, but am having trouble doing the same thing with the associated values.

//Works but no associated values
enum SportType: String, Decodable {
    case team, case individual
}

You need to create a custom init(from decoder: Decoder) throws , and treat your associated values like they are normal fields in the object, using CodingKeys to decode them. Details depend on how your associated values are actually represented in encoded object.

For example if your values are encoded as:

{ "team1": "aaa", "team2": "bbb" }

and

{ "individual": ["x", "y", "z"]}

You can:

enum SportType: Decodable {

    case team(String, String) //Two team names
    case individual([String]) //List of player names

    // Define associated values as keys
    enum CodingKeys: String, CodingKey {
        case team1
        case team2
        case individual
    }

    init(from decoder: Decoder) throws {

        let container = try decoder.container(keyedBy: CodingKeys.self)

        // Try to decode as team
        if let team1 = try container.decodeIfPresent(String.self, forKey: .team1),
           let team2 = try container.decodeIfPresent(String.self, forKey: .team2) {
            self = .team(team1, team2)
            return
        }

        // Try to decode as individual
        if let individual = try container.decodeIfPresent([String].self, forKey: .individual) {
            self = .individual(individual)
            return
        }

        // No luck
        throw DecodingError.dataCorruptedError(forKey: .individual, in: container, debugDescription: "No match")
    }
}

Test:

let encoded = """
    { "team1": "aaa", "team2": "bbb" }
""".data(using: .utf8)

let decoded = try? JSONDecoder().decode(SportType.self, from: encoded!)

switch decoded {
case .team(let a, let b):
    print("I am a valid team with team1=\(a), team2=\(b)")
case .individual(let a):
    print("I am a valid individual with individual=\(a)")
default:
    print("I was not parsed :(")
}

Prints:

I am a valid team with team1=aaa, team2=bbb

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM