大家好,我对Swift中的Encodable协议有个疑问。
我有以下json文件:
let magicJson = """
{
"value": [
{
"scheduleId": "magic@yahoo.com",
"somethingEventMoreMagical": "000220000"
}
]
}
""".data(using: .utf8)!
对于解码,我尝试避免创建两个都与Decodable一起使用的对象,第一个对象包含第二个对象的数组。我想将该对象压扁成这样:
struct MagicalStruct: Decodable {
private enum CodingKeys: String, CodingKey {
case value
}
private enum ScheduleCodingKeys: String, CodingKey {
case roomEmail = "scheduleId"
}
let roomEmail: String
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let magicContainer = try container.nestedContainer(keyedBy: ScheduleCodingKeys.self, forKey: .value)
roomEmail = try magicContainer.decode(String.self, forKey: ScheduleCodingKeys.roomEmail)
}
}
但是,当我尝试以下代码时:JSONDecoder().decode(MagicalStruct.self, magicJson)
我得到它期望一个数组但得到一个字典。另一方面,当我使用JSONDecoder().decode([MagicalStruct].self, magicJson)
时,得到的是它接收到一个数组但需要一个字典。
有人知道为什么会这样吗?
答案 0 :(得分:1)
首先,当您使用以下方法解码结构时:
JSONDecoder().decode(MagicalStruct.self, magicJson)
您正在尝试提取单个对象:let roomEmail: String
。
但是,您输入的JSON包含带有电子邮件的对象数组。这意味着您的代码:
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let magicContainer = try container.nestedContainer(keyedBy: ScheduleCodingKeys.self, forKey: .value)
roomEmail = try magicContainer.decode(String.self, forKey: ScheduleCodingKeys.roomEmail)
}
尝试解码一封电子邮件,而是有一个集合(在您的示例中为 one 元素-这就是为什么可能令人困惑的原因)。
您的错误Expected to decode Dictionary<String, Any> but found an array instead
也就在线了:
let magicContainer = try container.nestedContainer(keyedBy: ScheduleCodingKeys.self, forKey: .value)
您需要解码数组:
var magicContainer = try container.nestedUnkeyedContainer(forKey: .value)
但是,您有一个包含scheduleId
和somethingEventMoreMagical
键的对象数组。您想如何将所有值分配给一个let roomEmail: String
变量?
您可以改为解码字典:
let result = try JSONDecoder().decode([String: [MagicalStruct]].self, from: magicJson)
print(result["value"]!) // prints [MagicalStruct(roomEmail: "magic@yahoo.com")]
您可以简化您的MagicalStruct
:
struct MagicalStruct: Decodable {
enum CodingKeys: String, CodingKey {
case roomEmail = "scheduleId"
}
let roomEmail: String
}