ios - 允许 JSONDecoder() 接受空数组作为空字典

标签 ios json swift

给定这个结构:

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/

相关文章:

ios - JSON 文本未以数组或对象开头,并且未设置允许片段的选项

json - Http获取json转化为字符串

swift - 方法命名问题 Swift

ios - iOS模拟器无法播放声音效果

ios - iOS 13 中与历史相关的 Contacts 框架类有哪些?

ios - 如何在 Xcode/iOS 中设置 UIView 的标识符以调试自动布局?

javascript - 如何找到两个抽象节点之间的路径

json - VS402881 : No artifact version is specified corresponding to artifact source 'MyBuild.' Release Management vNext REST API

ios - 谷歌地图自动完成

ios - 从 firebase 存储获取图片