Swift 可解码,不';t解码具有无效值的可选枚举

Swift 可解码,不';t解码具有无效值的可选枚举,swift,swift4,codable,Swift,Swift4,Codable,我定义了如下枚举: enum ClubLevel: Int, Codable { case golden = 1, silver, bronze } 在我的结构中,我有一个类型为ClubLevel的可选属性,当我在init(来自解码器:解码器)中解码该属性时: 我面对这个错误: debugDescription:“无法从无效的Int值0初始化ClubLevel”,underlineError:nil“ 我想知道即使这个属性是可选的,解码器也不会继续 有什么想法吗?线路 self.clu

我定义了如下枚举:

enum ClubLevel: Int, Codable {
    case golden = 1, silver, bronze
}
在我的结构中,我有一个类型为
ClubLevel
的可选属性,当我在
init(来自解码器:解码器)
中解码该属性时:

我面对这个错误:

debugDescription:
“无法从无效的Int值0初始化ClubLevel”,underlineError: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)
    
    现在将:

  • 如果容器中缺少
    clubLevel
    键,则返回
    nil
    。如果它们的键存在
  • 尝试解码
    clubLevel
    键的
    nil
    (在JSON中表示为
    null
    )。如果不成功
  • 尝试解码
    ClubLevel
    键的
    ClubLevel
    。如果不成功
  • 出错
  • 这是编译器生成的
    init(from:)
    实现中解码选项的默认行为。在您的情况下,它仍然会引发错误,因为
    clubLevel
    键的值不是有效的
    clubLevel

    如果您只想尝试解码
    ClubLevel
    ,在解码过程中分配
    nil
    ,但由于任何原因(密钥丢失、无效值等),则需要使用
    try?

    self.clubLevel = try? container.decode(ClubLevel.self, forKey: .clubLevel)
    

    我遇到了同样的问题,我想我应该为任何感兴趣的人添加我的解决方案

    其思想是将枚举包装在以下结构中:

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

    我正在寻找一种方法来解决本文中描述的类似问题-仅针对枚举值数组而不是单个枚举值。由于这是在寻找此问题的答案时出现的第一篇文章,我想在这里分享我的解决方案,以防它可能会帮助有类似问题的人:)

    有问题的例子:

    {
      "exampleArray": [
         "FirstExample",
         "SecondExample",
         "abcde123",
         "FourthExample"
      ]
    }
    
    /。。。
    //用法
    init(来自解码器:解码器)抛出{
    let container=try decoder.container(keyedBy:CodingKeys.self)
    enumArray=try?container.decodeEnumArray([EnumType].self,forKey:.enumArray)
    }
    // ...
    扩展键DecodingContainer{
    func decodeEnumArray([T]。类型,forKey:Self.key)抛出->[T],其中T.RawValue:Decodable{
    返回try decode([T.RawValue].self,forKey:key).map{T(RawValue:$0)}.compactMap{$0}
    }
    }
    

    enumArray将成为
    [第一个示例,第二个示例,四个示例]

    谢谢你的评论@hamish你能解释一下
    try?container.decode(ClubLevel.self,forKey:.ClubLevel)
    try container.decode(ClubLevel?.self,forKey:.ClubLevel)之间的区别吗
    。您可以将您的评论作为答案发布。如果我们需要在任何失败原因上指定默认值,该怎么办?@Jan您可能希望使用nil聚合运算符,例如
    (try?container.decode(ClubLevel.self,forKey:.ClubLevel))??默认值
    啊,我缺少括号了。谢谢!
    
    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"
      ]
    }