问题描述
我的数据结构有一个枚举作为键,我希望下面的内容能自动解码。这是错误还是某些配置问题?
My data structure has an enum as a key, I would expect the below to decode automatically. Is this a bug or some configuration issue?
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)
}
我得到的错误是
推荐答案
问题是可以目前只能正确处理 String
和 Int
键。对于具有任何其他 Key
类型的字典(其中 Key
是 Encodable
/ 可解码
),它使用带有交替键值的 unkeyed 容器(JSON数组)进行编码和解码。
The problem is that Dictionary
's Codable
conformance can currently only properly handle String
and Int
keys. For a dictionary with any other Key
type (where that Key
is Encodable
/Decodable
), it is encoded and decoded with an unkeyed container (JSON array) with alternating key values.
因此,在尝试解码JSON时:
Therefore when attempting to decode the JSON:
{"dictionary": {"enumValue": "someString"}}
放入 AStruct
,字典
键的值应该是一个数组。
into AStruct
, the value for the "dictionary"
key is expected to be an array.
因此,
let jsonDict = ["dictionary": ["enumValue", "someString"]]
可以工作,并生成JSON:
would work, yielding the JSON:
{"dictionary": ["enumValue", "someString"]}
然后将其解码为:
AStruct(dictionary: [AnEnum.enumValue: "someString"])
但是,我真的认为 Dictionary
的 Co dable
一致性应该能够正确处理任何 CodingKey
一致性类型作为其 Key
( AnEnum
可以)-因为它可以使用该密钥进行编码和解码到带密钥的容器中(可以自由使用以请求此操作。)
However, really I think that Dictionary
's Codable
conformance should be able to properly deal with any CodingKey
conforming type as its Key
(which AnEnum
can be) – as it can just encode and decode into a keyed container with that key (feel free to file a bug requesting for this).
直到实施(如果有的话),我们总是可以构建一个包装器类型来做到这一点:
Until implemented (if at all), we could always build a wrapper type to do this:
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)
}
}
(或者只是将 CodableDictionary< AnEnum,String> 的code> dictionary 属性,并使用自动生成的 Codable
一致性–然后只需按照 dictionary.decoded
)
(or just have the dictionary
property of type CodableDictionary<AnEnum, String>
and use the auto-generated Codable
conformance – then just speak in terms of dictionary.decoded
)
来进行,按预期嵌套JSON对象:
Now we can decode the nested JSON object as expected:
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
(如果您希望给定值总是
Although that all being said, it could be argued that all you're achieving with a dictionary with an enum
as a key is just a struct
with optional properties (and if you expect a given value to always be there; make it non-optional).
因此,您可能只希望模型看起来像这样:
Therefore you may just want your model to look like:
struct BStruct : Codable {
var enumValue: String?
}
struct AStruct: Codable {
private enum CodingKeys : String, CodingKey {
case bStruct = "dictionary"
}
let bStruct: BStruct
}
哪个会很好用使用您当前的JSON:
Which would work just fine with your current 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")))
这篇关于Swift 4 Decodable-以枚举为键的字典的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!