对于给定的JSON,如下所示:
{
"store": {
"animals": [
{
"type": "dog"
},
{
"type": "cat"
}
]
}
}
我可以用枚举解析它,如下所示type
:
final class AnimalStore: Decodable {
let store: Store
}
extension AnimalStore {
struct Store: Decodable {
let animals: [Animal]
}
}
extension AnimalStore.Store {
struct Animal: Decodable {
let type: AnimalType?
}
}
extension AnimalStore.Store.Animal {
enum AnimalType: String, Decodable {
case dog = "dog"
case cat = "cat"
//case unknown = how such a case could be implemented?
}
}
而且因为它是可选的;如果 json 中缺少type
键值对,它会正常工作。
但是我想有另一种情况,让我们称之为unknown
这样,如果任何给定的类型不是狗或猫(字符串是其他东西(,类型将被初始化为未知。现在,如果给出狗或猫以外的类型,它会崩溃。
如何使用枚举实现给定类型以外的其他类型的初始化?
换句话说,对于给定的类型,例如:"type": "bird"
我希望type
初始化为unknown
.
用字符串添加枚举大小写,不妨使用"unknown"
。
要将不匹配的字符串转换为未知数,您必须在某个时候手动实现init(from decoder: Decoder)
,无论是在 Animal 还是在 AnimalType 中。我倾向于使用AnimalType,这样你就不必手动解码Animal的任何其他属性。
enum AnimalType: String, Decodable {
case dog = "dog"
case cat = "cat"
case unknown = "unknown"
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let string = try container.decode(String.self)
self = AnimalType(rawValue: string) ?? .unknown
}
}
如果你在Animal
中做到了,你需要这样的东西:
// Decode everything else...
type = try? decoder.decode(AnimalType.self, forKey: .type) ?? .unknown
如果你想允许一些枚举值的替代,你可以使用这样的东西:
enum Alt<S, A> {
case standard(S)
case alternative(A)
}
extension Alt: Decodable where S: Decodable, A: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let standard = try? container.decode(S.self) {
self = .standard(standard)
} else if let alternative = try? container.decode(A.self) {
self = .alternative(alternative)
} else {
throw DecodingError.typeMismatch(
Self.self,
DecodingError.Context(codingPath: container.codingPath, debugDescription: "")
)
}
}
}
然后将AnimalStore.Store.Animal
声明更改为:
extension AnimalStore.Store {
struct Animal: Decodable {
let type: Alt<AnimalType, String>?
}
}
现在它将首先尝试将其解码为AnimalType
,然后,如果失败,则将其解码为alternative
类型。因此,您可以将字符串的值保留在枚举中。
编辑:或者在alternative
RawValue
standard
的情况下,您可以使用这样的东西:
enum RawBox<T>: RawRepresentable where T: RawRepresentable {
typealias RawValue = T.RawValue
case packed(T)
case raw(RawValue)
init(rawValue: Self.RawValue) {
if let packed = T(rawValue: rawValue) {
self = .packed(packed)
} else {
self = .raw(rawValue)
}
}
var rawValue: T.RawValue {
switch self {
case .packed(let packed):
return packed.rawValue
case .raw(let raw):
return raw
}
}
}
extension RawBox: Decodable where RawValue: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let raw = try container.decode(RawValue.self)
self.init(rawValue: raw)
}
}
extension RawBox: Encodable where RawValue: Encodable {
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(self.rawValue)
}
}
。
extension AnimalStore.Store {
struct Animal: Decodable {
let type: RawBox<AnimalType>?
}
}
我想你可以试试这个
extension AnimalStore.Store {
struct Animal: Decodable {
let type: AnimalType?
enum CodingKeys: String, CodingKey {
case type
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
type = try? values.decode(AnimalType.self, forKey: .type) ?? .unknown
}
}
}
extension AnimalStore.Store.Animal {
enum AnimalType: String {
case dog
case cat
case unknown
}
}