我的数据结构有一个枚举作为键,我希望以下内容能自动解码。这是错误还是某些配置问题?
import Foundation enum AnEnum: String, Codable { case enumValue } struct AStruct: Codable { let dictionary: [AnEnum: String] } let jsonDict = ["dictionary": ["enumValue": "someString"]] let data = try! JSONSerialization.data(withJSONObject: jsonDict, options: .prettyPrinted) let decoder = JSONDecoder() do { try decoder.decode(AStruct.self, from: data) } catch { print(error) }
我得到的错误是,这似乎使字典与数组混淆。
typeMismatch(Swift.Array,Swift.DecodingError.Context(codingPath:[可选(__lldb_expr_85.AStruct。(_ 0E2FD0A9B523101D0DCD67578F72D1DD中的CodingKeys)。
问题是,Dictionary的Codable一致性目前只能正确处理String和Int键。用于与任何其他一个字典Key类型(如该Key是Encodable/ Decodable),其被编码,并用解码 unkeyed的 容器(JSON阵列)具有交替的键值。
Dictionary
Codable
String
Int
Key
Encodable
Decodable
因此,当尝试解码JSON时:
{"dictionary": {"enumValue": "someString"}}
into中AStruct,"dictionary"键的值应为数组。
AStruct
"dictionary"
所以,
let jsonDict = ["dictionary": ["enumValue", "someString"]]
将工作,产生JSON:
{"dictionary": ["enumValue", "someString"]}
然后将其解码为:
AStruct(dictionary: [AnEnum.enumValue: "someString"])
但是,实际上,我认为Dictionary的Codable一致性 应该 能够正确地处理任何CodingKey符合类型Key(AnEnum可以是)–因为它可以使用该密钥进行编码和解码到带密钥的容器中(可以自由地提交请求这个)。
CodingKey
AnEnum
在实施之前(如果有的话),我们始终可以构建包装器类型来执行此操作:
struct CodableDictionary<Key : Hashable, Value : Codable> : Codable where Key : CodingKey { let decoded: [Key: Value] init(_ decoded: [Key: Value]) { self.decoded = decoded } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: Key.self) decoded = Dictionary(uniqueKeysWithValues: try container.allKeys.lazy.map { (key: $0, value: try container.decode(Value.self, forKey: $0)) } ) } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: Key.self) for (key, value) in decoded { try container.encode(value, forKey: key) } } }
然后像这样实现:
enum AnEnum : String, CodingKey { case enumValue } struct AStruct: Codable { let dictionary: [AnEnum: String] private enum CodingKeys : CodingKey { case dictionary } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) dictionary = try container.decode(CodableDictionary.self, forKey: .dictionary).decoded } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(CodableDictionary(dictionary), forKey: .dictionary) } }
(或仅具有dictionarytype属性CodableDictionary<AnEnum, String>并使用自动生成的Codable一致性-然后用表示dictionary.decoded)
dictionary
CodableDictionary<AnEnum, String>
dictionary.decoded
现在我们可以按预期解码嵌套的JSON对象:
let data = """ {"dictionary": {"enumValue": "someString"}} """.data(using: .utf8)! let decoder = JSONDecoder() do { let result = try decoder.decode(AStruct.self, from: data) print(result) } catch { print(error) } // AStruct(dictionary: [AnEnum.enumValue: "someString"])
尽管说了这么多,但可以说,用enum键作为关键字的字典所能实现的一切仅仅是struct具有可选属性的(并且,如果您希望给定值始终存在,则使其变为非可选)。
enum
struct
因此,您可能只希望模型看起来像:
struct BStruct : Codable { var enumValue: String? } struct AStruct: Codable { private enum CodingKeys : String, CodingKey { case bStruct = "dictionary" } let bStruct: BStruct }
这将与您当前的JSON一起正常工作:
let data = """ {"dictionary": {"enumValue": "someString"}} """.data(using: .utf8)! let decoder = JSONDecoder() do { let result = try decoder.decode(AStruct.self, from: data) print(result) } catch { print(error) } // AStruct(bStruct: BStruct(enumValue: Optional("someString")))