ios - 允许 JSONDecoder() 接受空数组作为空字典
问题描述
鉴于此结构:
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()
. 弹性有一个很好的解释。
也就是说,如果您无法更正服务器,则可以在客户端进行修复。这里的许多答案都是基于尝试解码该值,如果失败则假设它是一个空数组。这行得通,但这意味着意外的 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 不同。)
推荐阅读
- python - 如何永久制作变量存储,因此当我单击运行时它不会重新启动到原始状态 - python
- scala - http4s 路由模式匹配中的无法访问代码警告
- python - 使用 python 请求模块从 HTML 表单发布/获取结果
- asp.net-core - 下载文件或使用 axios 和 ASP.Net core 2.2 在单个 API 调用中发送 json 结果?
- c# - 使用 mysql 和 c# 编程语言搜索存在的时间
- reactjs - Atom & eslint:找不到模块 'eslint-config-react-app'
- javascript - 在 Firestore 中接受好友请求功能?
- python - 是否可以在 tkinter 中更改菜单边框颜色
- flutter - 如何制作自定义下拉菜单?
- vue.js - 如何在 vuejs 中创建绑定 img src,因为这将上传到 AEM