Swift中枚举作为字典键解码失败:为何报类型不匹配错误?
问题原因
JSONDecoder默认不支持直接将JSON字典解码为以自定义枚举作为Key的Swift字典。尽管你的枚举是RawRepresentable(基于String类型),但枚举默认的Decodable实现仅用于解码单个枚举值,而非作为字典Key处理,这导致解码器错误地将目标类型识别为数组,进而抛出类型不匹配的错误。
解决方案
方法一:先解码为[String: String]再转换(推荐)
先将JSON解码为基础的字符串键字典,再手动将键转换为枚举类型,这种方式简单直观,适合大多数场景:
enum Lg: String, CaseIterable, Decodable { case deutsch = "de" case english = "en" } let test = "{ \"de\": \"Bisasam\", \"en\": \"Bulbasaur\" }" let data = test.data(using: .utf8)! do { // 第一步:解码为[String: String] let stringDict = try JSONDecoder().decode([String: String].self, from: data) // 第二步:转换为[Lg: String],自动过滤无法匹配的键 let result = Dictionary(uniqueKeysWithValues: stringDict.compactMap { key, value in guard let lgKey = Lg(rawValue: key) else { // 可选:处理无效键,比如打印日志或抛出错误 print("忽略无效键:\(key)") return nil } return (lgKey, value) }) print(result) // 输出:[Lg.deutsch: "Bisasam", Lg.english: "Bulbasaur"] } catch { print(error) }
方法二:自定义字典的Decodable实现(适合频繁使用的场景)
如果你需要频繁解码这种枚举键字典,可以自定义[Lg: String]的Decodable实现,让解码器直接支持该类型:
enum Lg: String, CaseIterable, Decodable { case deutsch = "de" case english = "en" } // 辅助CodingKey类型,用于处理RawRepresentable枚举键 struct RawEnumCodingKey<K: RawRepresentable>: CodingKey where K.RawValue == String { let stringValue: String let intValue: Int? init?(stringValue: String) { self.stringValue = stringValue self.intValue = nil } init?(intValue: Int) { return nil } var rawEnum: K? { return K(rawValue: stringValue) } } // 自定义[Lg: String]的Decodable实现 extension Dictionary where Key == Lg, Value == String: Decodable { public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: RawEnumCodingKey<Lg>.self) var resultDict = [Lg: String]() for key in container.allKeys { guard let lgKey = key.rawEnum else { throw DecodingError.dataCorrupted(.init( codingPath: [key], debugDescription: "无效的语言键:\(key.stringValue)" )) } let value = try container.decode(String.self, forKey: key) resultDict[lgKey] = value } self = resultDict } } // 使用示例 let test = "{ \"de\": \"Bisasam\", \"en\": \"Bulbasaur\" }" let data = test.data(using: .utf8)! do { let result = try JSONDecoder().decode([Lg: String].self, from: data) print(result) } catch { print(error) }
内容的提问来源于stack exchange,提问作者JamesDerrick248
相关产品推荐
相关产品推荐

