Swift - 从 json 字符串解码的枚举有一个未知的大小写,不包含在大小写中
Swift - Have an unknown case for enum decoded from json string that is not included in cases
对于给定的 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
of 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
}
}
对于给定的 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
of 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
}
}