给定这个结构:
public struct Error: Codable {
public let code: String
public let message: String
public let params: [String: String]?
}
还有这个 JSON:
[
{
"message" : "The requested user could not be found.",
"params" : [],
"code" : "requested_user_not_found"
}
]
有没有办法使用 Swift 中的 JSONDecoder() 类对其进行解码? params
键应该是一个字典,但由于生成此 JSON 的外部 API 的实现方式(在 PHP 中),空字典在 JSON 中呈现为空数组。
目前,尝试将提供的 JSON 解码为提供的结构的实例会导致抛出错误。
最佳答案
首先,PHP 不需要这样做,因此如果可能的话应该更正 PHP。 PHP中表达“空对象”的方法是new\stdClass()
。 Elastic has a nice explanation.
也就是说,如果您无法纠正服务器,您可以在客户端修复它。这里的许多答案都是基于尝试解码该值,如果失败则假设它是一个空数组。这可行,但这意味着意外的 JSON 不会生成好的错误。相反,我会将这个问题提取到一个函数中:
/// Some PHP developers emit [] to indicate empty object rather than using stdClass().
/// This returns a default value in that case.
extension KeyedDecodingContainer {
func decodePHPObject<T>(_ type: T.Type, forKey key: Key, defaultValue: T) throws -> T
where T : Decodable
{
// Sadly neither Void nor Never conform to Decodable, so we have to pick a random type here, String.
// The nicest would be to decode EmptyCollection, but that also doesn't conform to Decodable.
if let emptyArray = try? decode([String].self, forKey: key), emptyArray.isEmpty {
return defaultValue
}
return try decode(T.self, forKey: key)
}
// Special case the common dictionary situation.
func decodePHPObject<K, V>(_ type: [K: V].Type, forKey key: Key) throws -> [K: V]
where K: Codable, V: Codable
{
return try decodePHPObject([K:V].self, forKey: key, defaultValue: [:])
}
}
这提供了一个 .decodePHPObject(_:forKey:)
方法,您可以在自定义解码器中使用该方法。
public struct ErrorValue: Codable {
public let code: String
public let message: String
public let params: [String: String]
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.code = try container.decode(String.self, forKey: .code)
self.message = try container.decode(String.self, forKey: .message)
// And use our new decodePHPObject.
self.params = try container.decodePHPObject([String: String].self, forKey: .params)
}
}
(我已重命名此 ErrorValue
以消除与 stdlib 类型 Error
的冲突,并且我已将 params
设为非可选因为通常不应该有可选集合,除非“空”的处理方式与 nil 不同。)
关于ios - 允许 JSONDecoder() 接受空数组作为空字典,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58404670/