Swift - 有一个未知的枚举案例,从 json 字符串解码,该案例不包括在案例中



对于给定的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类型。因此,您可以将字符串的值保留在枚举中。


编辑:或者在alternativeRawValuestandard的情况下,您可以使用这样的东西:

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
}
}

最新更新