可解码,不解码具有无效值的可选枚举
Decodable, doesn't decode optional enum with invalid value
我定义了这样一个枚举:
enum ClubLevel: Int, Codable {
case golden = 1, silver, bronze
}
在我的结构中,我有一个类型为 ClubLevel
的可选 属性,当我在 init(from decoder: Decoder)
中解码此 属性 时:
self.clubLevel = try container.decode(ClubLevel?.self, forKey: .clubLevel)
我遇到这个错误:
调试说明:"Cannot initialize ClubLevel from invalid Int value 0", underlyingError: nil"
我想知道即使这个 属性 是可选的,解码器也不会继续
有什么想法吗?
行
self.clubLevel = try container.decode(ClubLevel?.self, forKey: .clubLevel)
不会尝试解码 ClubLevel
,如果不成功则分配 nil
。它的作用是:
- 尝试为
clubLevel
密钥解码 nil
(在 JSON 中表示为 null
)。如果不成功,
- 尝试为
clubLevel
密钥解码 ClubLevel
。如果不成功,
- 抛出错误
因此,如果 clubLevel
键的值既不是 nil
也不是有效的 ClubLevel
表示,您将抛出错误。您会注意到,这也意味着如果 clubLevel
键完全丢失(而不是以 nil
的值出现),您将抛出一个错误。
通过 decodeIfPresent
:
完成忽略丢失的键
self.clubLevel = try container.decodeIfPresent(ClubLevel.self, forKey: .clubLevel)
现在将:
- Return
nil
如果容器中缺少 clubLevel
密钥。如果他们的密钥存在,
- 尝试为
clubLevel
密钥解码 nil
(在 JSON 中表示为 null
)。如果不成功,
- 尝试为
clubLevel
密钥解码 ClubLevel
。如果不成功,
- 抛出错误
这是编译器生成的 init(from:)
实现中解码可选值的默认行为。它仍然会在您的情况下引发错误,因为 clubLevel
键的值不是有效的 ClubLevel
.
如果您只想尝试解码 ClubLevel
,请在因 任何 原因(密钥丢失、无效值、等),那么你想使用 try?
:
self.clubLevel = try? container.decode(ClubLevel.self, forKey: .clubLevel)
我遇到了同样的问题,我想我会为任何感兴趣的人添加我的解决方案。
想法是将枚举包装在以下 struct
:
中
struct OptionalDecodableEnum<T>: Decodable where T: RawRepresentable, T.RawValue: Decodable {
let value: T?
init(from decoder: Decoder) throws {
value = T(rawValue: try decoder.singleValueContainer().decode(T.RawValue.self))
}
}
主要好处是您不必在每次需要可选枚举时都实施 Decodable
。您也不需要带有额外括号的可选链接。
但是,您必须 return 内部 value
属性 才能使用它。例如
struct Foo: Decodable {
enum ClubLevel: Int, Codable {
case golden = 1, silver, bronze
}
let clubLevel: OptionalDecodableEnum<ClubLevel>
}
let foo = try jsonDecoder.decode(Foo.self, from: data)
print(String(describing: foo.clubLevel.value))
我一直在寻找一种方法来解决此 post 中描述的类似问题 - 仅针对枚举值数组而不是单个枚举值。由于这是第一个 post 在寻找这个问题的答案时出现,所以我想在这里分享我的解决方案,以防它可以帮助遇到类似问题的人:)
有问题的例子:
{
"exampleArray": [
"FirstExample",
"SecondExample",
"abcde123",
"FourthExample"
]
}
// ...
// Usage
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
enumArray = try? container.decodeEnumArray([EnumType].self, forKey: .enumArray)
}
// ...
extension KeyedDecodingContainer {
func decodeEnumArray<T: RawRepresentable>(_: [T].Type, forKey key: Self.Key) throws -> [T] where T.RawValue: Decodable {
return try decode([T.RawValue].self, forKey: key).map { T(rawValue: [=11=]) }.compactMap { [=11=] }
}
}
enumArray 将是 [FirstExample, SecondExample, FourthExample]
我定义了这样一个枚举:
enum ClubLevel: Int, Codable {
case golden = 1, silver, bronze
}
在我的结构中,我有一个类型为 ClubLevel
的可选 属性,当我在 init(from decoder: Decoder)
中解码此 属性 时:
self.clubLevel = try container.decode(ClubLevel?.self, forKey: .clubLevel)
我遇到这个错误:
调试说明:"Cannot initialize ClubLevel from invalid Int value 0", underlyingError: nil"
我想知道即使这个 属性 是可选的,解码器也不会继续
有什么想法吗?
行
self.clubLevel = try container.decode(ClubLevel?.self, forKey: .clubLevel)
不会尝试解码 ClubLevel
,如果不成功则分配 nil
。它的作用是:
- 尝试为
clubLevel
密钥解码nil
(在 JSON 中表示为null
)。如果不成功, - 尝试为
clubLevel
密钥解码ClubLevel
。如果不成功, - 抛出错误
因此,如果 clubLevel
键的值既不是 nil
也不是有效的 ClubLevel
表示,您将抛出错误。您会注意到,这也意味着如果 clubLevel
键完全丢失(而不是以 nil
的值出现),您将抛出一个错误。
通过 decodeIfPresent
:
self.clubLevel = try container.decodeIfPresent(ClubLevel.self, forKey: .clubLevel)
现在将:
- Return
nil
如果容器中缺少clubLevel
密钥。如果他们的密钥存在, - 尝试为
clubLevel
密钥解码nil
(在 JSON 中表示为null
)。如果不成功, - 尝试为
clubLevel
密钥解码ClubLevel
。如果不成功, - 抛出错误
这是编译器生成的 init(from:)
实现中解码可选值的默认行为。它仍然会在您的情况下引发错误,因为 clubLevel
键的值不是有效的 ClubLevel
.
如果您只想尝试解码 ClubLevel
,请在因 任何 原因(密钥丢失、无效值、等),那么你想使用 try?
:
self.clubLevel = try? container.decode(ClubLevel.self, forKey: .clubLevel)
我遇到了同样的问题,我想我会为任何感兴趣的人添加我的解决方案。
想法是将枚举包装在以下 struct
:
struct OptionalDecodableEnum<T>: Decodable where T: RawRepresentable, T.RawValue: Decodable {
let value: T?
init(from decoder: Decoder) throws {
value = T(rawValue: try decoder.singleValueContainer().decode(T.RawValue.self))
}
}
主要好处是您不必在每次需要可选枚举时都实施 Decodable
。您也不需要带有额外括号的可选链接。
但是,您必须 return 内部 value
属性 才能使用它。例如
struct Foo: Decodable {
enum ClubLevel: Int, Codable {
case golden = 1, silver, bronze
}
let clubLevel: OptionalDecodableEnum<ClubLevel>
}
let foo = try jsonDecoder.decode(Foo.self, from: data)
print(String(describing: foo.clubLevel.value))
我一直在寻找一种方法来解决此 post 中描述的类似问题 - 仅针对枚举值数组而不是单个枚举值。由于这是第一个 post 在寻找这个问题的答案时出现,所以我想在这里分享我的解决方案,以防它可以帮助遇到类似问题的人:)
有问题的例子:
{
"exampleArray": [
"FirstExample",
"SecondExample",
"abcde123",
"FourthExample"
]
}
// ...
// Usage
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
enumArray = try? container.decodeEnumArray([EnumType].self, forKey: .enumArray)
}
// ...
extension KeyedDecodingContainer {
func decodeEnumArray<T: RawRepresentable>(_: [T].Type, forKey key: Self.Key) throws -> [T] where T.RawValue: Decodable {
return try decode([T.RawValue].self, forKey: key).map { T(rawValue: [=11=]) }.compactMap { [=11=] }
}
}
enumArray 将是 [FirstExample, SecondExample, FourthExample]