Ответ 1
Проблема в том, что Dictionary
Codable
соответствие может в настоящее время корректно обрабатывать ключи String
и Int
. Для словаря с любым другим типом Key
(где Key
есть Encodable
/Decodable
), он кодируется и декодируется с помощью неключевого контейнера (массив JSON) со значениями переменных ключей.
Поэтому при попытке декодирования JSON:
{"dictionary": {"enumValue": "someString"}}
в AStruct
, ожидается, что значение для ключа "dictionary"
будет массивом.
Итак,
let jsonDict = ["dictionary": ["enumValue", "someString"]]
будет работать, что даст JSON:
{"dictionary": ["enumValue", "someString"]}
который затем будет декодирован в:
AStruct(dictionary: [AnEnum.enumValue: "someString"])
Однако, действительно, я думаю, что соответствие Dictionary
Codable
должно иметь возможность правильно обрабатывать любой CodingKey
соответствующий тип как его Key
(который может быть 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)
}
}
(или просто иметь свойство Dictionary
типа CodableDictionary<AnEnum, String>
и использовать автогенерированное соответствие Codable
), а затем просто говорить в терминах 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
с дополнительными свойствами (и если вы ожидаете, что заданное значение всегда будет там, сделайте это необязательным).
Поэтому вы можете просто хотеть, чтобы ваша модель выглядела так:
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")))