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
。它的作用是:
nil
(在 JSON 中表示为
null
)以获得
clubLevel
键。如果不成功,
ClubLevel
解码为
clubLevel
键。如果不成功,
clubLevel
键的值既不是
nil
也不是有效的
ClubLevel
表示形式,则会抛出错误。您会注意到,这也意味着如果
clubLevel
键完全丢失(而不是存在
nil
值),您将会抛出错误。通过
decodeIfPresent
忽略丢失的键:
self.clubLevel = try container.decodeIfPresent(ClubLevel.self, forKey: .clubLevel)
现在将:
如果容器中缺少
nil
clubLevel
。如果钥匙存在,尝试解码 nil
null
)以获得 clubLevel
键。如果不成功,尝试将 ClubLevel
clubLevel
键。如果不成功,抛出错误init(from:)
实现中解码选项的默认行为。在您的情况下,它仍然会抛出错误,因为
clubLevel
键的值不是有效的 ClubLevel
。如果您只想尝试解码
ClubLevel
,在因任何
原因(密钥缺失、值无效等)而失败的解码上分配
nil
,那么您需要使用
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
。您也不需要带有额外括号的可选链接。
但是,当您想使用它时,您必须返回内部
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))
我正在寻找一种方法来解决本文中描述的类似问题 - 只是针对枚举值数组而不是单个枚举值。由于这是在寻找这个问题的答案时出现的第一篇文章,我想在这里分享我的解决方案,以防它可以帮助有类似问题的人:)
{
"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]
我能够将 Guy Kogus 的答案转换为属性包装器,这消除了使用此代码时的一些摩擦。
@propertyWrapper struct OptionalCodableEnum<T>: Codable where T: RawRepresentable, T.RawValue: Codable {
var wrappedValue: T?
init(wrappedValue: T?) {
self.wrappedValue = wrappedValue
}
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let rawValue = try? container.decode(T.RawValue.self),
let result = T(rawValue: rawValue) {
wrappedValue = result
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(wrappedValue?.rawValue)
}
}
用法如下:
struct MyCodable: Codable {
enum MyCodableEnum: Int, Codable {
case first = 0, second
}
@OptionalCodableEnum
var anEnumValue: MyCodableEnum?
}