我已经定义了这样的枚举:
enum ClubLevel: Int, Codable {
case golden = 1, silver, bronze
}
在我的struct中我有一个ClubLevel
类型的可选属性,当我在init(from decoder: Decoder)
中解码这个属性时:
self.clubLevel = try container.decode(ClubLevel?.self, forKey: .clubLevel)
我遇到这个错误:
debugDescription:"Cannot initialize ClubLevel from invalid Int value 0", underlyingError: nil"
我想知道即使这个属性是可选的,解码器也不会继续
任何想法?
答案 0 :(得分:6)
该行
self.clubLevel = try container.decode(ClubLevel?.self, forKey: .clubLevel)
不会尝试解码ClubLevel
,如果不成功则指定nil
。它的作用是:
nil
密钥解码null
(在JSON中表示为clubLevel
)。如果不成功,ClubLevel
密钥解析clubLevel
。如果不成功,因此,如果clubLevel
键的值既不是nil
也不是有效ClubLevel
表示,那么您将收到错误。您会注意到,这也意味着如果clubLevel
密钥完全丢失(而不是以nil
的值存在),您将收到错误。
使用decodeIfPresent
:
self.clubLevel = try container.decodeIfPresent(ClubLevel.self, forKey: .clubLevel)
现在:
nil
密钥,请返回clubLevel
。如果他们的钥匙存在,nil
密钥解码null
(在JSON中表示为clubLevel
)。如果不成功,ClubLevel
密钥解析clubLevel
。如果不成功,这是在编译器生成的init(from:)
实现中解码选项的默认行为。由于clubLevel
密钥的值不是有效ClubLevel
,因此在您的情况下仍然会出错。
如果您只想尝试解码ClubLevel
,请为任何原因(密钥丢失,无效值等)的解码失败分配nil
,然后您想使用try?
:
self.clubLevel = try? container.decode(ClubLevel.self, forKey: .clubLevel)
答案 1 :(得分:1)
我遇到了同样的问题,并认为我会为有兴趣的人添加解决方案。
想法是将枚举包装在以下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
。您也不需要带额外括号的可选链接。
但是,当您想使用内部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))
答案 2 :(得分:0)
我一直在寻找一种解决本文所述类似问题的方法-仅用于枚举值数组,而不是单个枚举值。因为这是寻找该问题的答案时出现的第一篇文章,所以我想在这里分享我的解决方案,以防它可能帮助有类似问题的人:)
问题示例:
{
"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: $0) }.compactMap { $0 }
}
}
enumArray将为[FirstExample, SecondExample, FourthExample]