因为
[AnyHashable: Any]
(我需要它)我必须实施init(from:)
和encode(to:)
。但是当我运行它时,它无法解码具有数组值的属性:
typeMismatch(Swift.String, Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "items", intValue: nil)], debugDescription: "Expected to decode String but found an array instead.", underlyingError: nil))
这是您可以在 Playground 中运行的代码:
struct ServerResponse: Codable {
var headers: [AnyHashable: Any]?
var items: [Item]?
enum CodingKeys: String, CodingKey {
case items, headers
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: ServerResponse.CodingKeys.self)
items = try container.decode([Item].self, forKey: ServerResponse.CodingKeys.items)
let singleValueContainer = try decoder.singleValueContainer()
let stringDictionary = try singleValueContainer.decode([String: String].self)
headers = [:]
for (key, value) in stringDictionary {
headers?[key] = value
}
}
public func encode(to encoder: Encoder) throws {
let stringDictionary: [String: String] = Dictionary(
uniqueKeysWithValues: headers?.map {("\($0)", "\($1)")} ?? []
)
var singleValueContainer = encoder.singleValueContainer()
try singleValueContainer.encode(stringDictionary)
var container = encoder.container(keyedBy: ServerResponse.CodingKeys.self)
try container.encode(items, forKey: ServerResponse.CodingKeys.items)
}
struct Item: Codable {
let name: String
}
}
let testData = """
{
"items": [
{"name": "John"},
{"name": "Duo"}
]
}
""".data(using: .utf8)!
let decoder = JSONDecoder()
do {
let response = try decoder.decode(ServerResponse.self, from: testData)
print(response)
} catch {
print(error)
}
有什么问题吗?为什么它在我放置数组时抱怨得到
String
?
如果我从结构中删除标头并符合Codable
一切正常。
这里的问题在于您如何通过尝试将其解码为字典来尝试从该顶级词典中提取项目。具体来说,
let singleValueContainer = try decoder.singleValueContainer()
let stringDictionary = try singleValueContainer.decode([String: String].self)
是有问题的片段。使用您特定的 JSON 负载,
singleValueContainer
到此结束
{
"items": [ ... ],
"..." // <- assuming there are other actual keys and values
}
这样做是有效的,但是当您尝试将容器的内容解码为
[String: String]
时,您是断言您希望容器专门包含一个包含String
键和String
值的字典;然而,items
键的值是not 一个字符串,而是一个数组。
当你有一个包含任意值的集合时,提取其内容的正确方法是使用键控容器。具体来说,您可以使用带键的容器,其键类型可以采用 any
String
或 Int
值,例如:
struct AnyCodingKey: CodingKey {
let intValue: Int?
let stringValue: String
init?(intValue: Int) {
self.intValue = intValue
self.stringValue = "\(intValue)"
}
init?(stringValue: String) {
intValue = Int(stringValue)
self.stringValue = stringValue
}
}
有了这种编码密钥类型,您就可以请求
decoder
作为另一个带密钥的容器——这一次,密钥可以是任意的:
let untypedContainer = try decoder.container(keyedBy: AnyCodingKey.self)
诀窍是现在,untypedContainer
中的
values在您尝试对其进行解码之前不会被断言为任何类型。然后,您可以迭代
untypedContainer.allKeys
(与现在迭代 stringDictionary
相同),并且对于每个键,您可以决定如何从容器中 decode(_:forKey:)
。你可以:
String
,如果你得到一个DecodinerError.typeMismatch
错误,就跳过键值对