【问题标题】:Swift Decode [String: Any]快速解码 [字符串:任意]
【发布时间】:2019-03-07 15:15:58
【问题描述】:

所以我有这个 API 可以返回 [String: Any] 的字典,我知道 Any 是 DecodableDecodable 的数组,但是我终生无法弄清楚如何采取该字典并将其解码为某种结构:

我所拥有的基本上是这样的:

public func call<T: Codable> (completion handler: @escaping (T?) -> ()) {
    let promise = api.getPromise ()
    promise.done (on: DispatchQueue.main, { (results: [String:Any])
        let decodedResults:T? = results.decode (as: T.self) // <-- this is what I want
        handler (decodedResults)
    })
}

我尝试将其转换为数据,然后使用以下代码对其进行解码:

let values = results.compactMap { $0.value }
let data = JSONSerialization.data (withJSONObject: values, options: [])
let decodedResult = JSONDecoder().decode(T.self, from: data)

但它总是以NSInvalidArgumentException 失败,知道如何解决这个问题吗?

我试图实现但未能做到的另一件事是将值转换为元组,但我发现动态创建元组是不可能的。

【问题讨论】:

  • 您应该更改您的 api.getPromise() 方法以返回 data 承诺,而不是 [String: Any]
  • 这不是一个真正的选择,因为api已经将不是由swift原语组成的数据解码为几个swift结构,还有其他方法吗,或者我别无选择,只能去@ 987654330@
  • 我也需要这个!除了我需要对其进行编码

标签: swift encoding decoding


【解决方案1】:

解码器将数据转换为可解码值。它们与[String: Any] 类型或任何其他非数据类型没有任何关系。所以如果你想通过解码器运行它,你需要将它转换为 JSON 编码成 Data。

如果 [String: Any] 结果是专门的 JSONSerialization 安全类型(数组、字典、字符串、数字、null),那么 JSONSerialization.data(withJSONObject:options:) 会让您返回数据,因此您可以重新解码它。你的代码不仅重新编码它的结果,它首先把它变成一个数组:

let values = results.compactMap { $0.value }
let data = JSONSerialization.data (withJSONObject: values, options: [])

这很奇怪。您真的是要在这里创建一个数组并扔掉键吗?然后我希望您的JSONDecoder().decode() 行解码[T].self 而不是T.self。所以我希望下面的代码(前提是你的 [String: Any] 是 JSON 安全的):

public func call<T: Decodable>(completion handler: @escaping (T?) -> ()) {
    let promise = api.getPromise()
    promise.done(on: .main) { (results: [String:Any]) in
        guard JSONSerialization.isValidJSONObject(results) else {
            handler(nil)
            return
        }

        let data = JSONSerialization.data(withJSONObject: results)
        let decodedResults = try? JSONDecoder().decode(T.self, from: data)
        handler(decodedResults)
    }
}

在 cmets 中,您注意到解码数据([String: Any])不是由原语组成的。在这种情况下,无法使用 JSONSerialization 对其进行重新编码。您需要将[String: Any] 传递给知道如何处理它的东西。例如:

protocol DictionaryDecodable {
    init?(dictionary: [String: Any])
}

public func call<T: DictionaryDecodable>(completion handler: @escaping (T?) -> ()) {
    let promise = api.getPromise ()
    promise.done(on: .main) { (results: [String:Any])
        handler(T.init(dictionary: results))
    }
}

您的类型将需要实现一个init?(dictionary:),它可以从[String: Any] 中解码出它们自己的值。

【讨论】:

  • 真遗憾,我真的想要一种更优雅的方法来解决它,但我已经有一种感觉,那就是唯一的方法,谢谢!
猜你喜欢
  • 2023-02-22
  • 2023-03-29
  • 2015-03-17
  • 2020-03-26
  • 2020-02-28
  • 2011-03-11
  • 1970-01-01
  • 1970-01-01
  • 2022-07-25
相关资源
最近更新 更多