对于 Mobile Armor,它可能需要好几个驾驶员负责四肢和头部,但对于 Mobile Suit,上头则更在乎它们有多少只脚,以及是不是高达。当我们直接加上 Codable,Compiler 就报错了,显然我们需要手动做一些什么奇怪的事情才行。
易知,Compiler 没有恰当的手段给我们自动生成 CodingKey,对于普通的 Enum,我们可以指定 Enum 的 raw value 的类型来解决问题,但带有 associated values 的 Enum 则不允许我们这么做,所以我们得手动添加 CodingKey:
1 2 3 4 5
privateenumCodingKeys: String, CodingKey{ case type case numberOfLegs = "number-of-legs", isGundam = "is-gundam" case numberOfPilots = "number-of-pilots" }
然后我们还需要实现 init(from decoder: Decoder):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) let type = try container.decode(String.self, forKey: .type) switch type { case"mobile-suit": let numberOfLegs = try values.decode(Int.self, forKey: .numberOfLegs) let isGundam = try values.decode(Bool.self, forKey: .isGundam) self = .mobileSuit(numberOfLegs: numberOfLegs, isGundam: isGundam) case"mobile-armor": let numberOfPilots = try values.decode(Int.self, forKey: .numberOfPilots) self = .mobileArmor(numberOfPilots: numberOfPilots) default: throwUnitError.decoding("error") } }
这样把不同 case 的 coding keys 都放在一起是比较危险的,即便是新人类或者调整者,也不能保证不会手残,在生成 MS 资料的时候去读取了一个不存在的、属于 MA 的 key。所以要是想再 type safe 一些的话,可以把不同 case 的 coding keys 放在不同的 Enum 里面,防止敲错 key:
1 2 3 4 5 6 7 8 9 10 11 12
privateenumMobileArmorCodingKeys: String, CodingKey{ case numberOfPilots = "number-of-pilots" }
let decoder = JSONDecoder() let result = try! decoder.decode([Unit].self, from: json.data(using: .utf8)!) // [Unit.mobileSuit(numberOfLegs: 4, isGundam: true), Unit.mobileArmor(2)] let encoder = JSONEncoder() let data = try! encoder.encode(result) // [{"type":"mobile-suit","number-of-legs":4,"is-gundam":true},{"type":"mobile-armor","number-of-pilots":2}]