gpt4 book ai didi

json - 如何将 Swift JSONDecode 与动态类型一起使用?

转载 作者:搜寻专家 更新时间:2023-10-30 22:37:00 25 4
gpt4 key购买 nike

我的应用程序有一个本地缓存并从/向服务器发送/接收模型。所以我决定构建一个映射 [String : Codable.Type],主要是为了能够解码我在这个通用缓存上拥有的任何内容,无论是本地创建的还是从服务器接收的。

let encoder = JSONEncoder()
let decoder = JSONDecoder()
var modelNameToType = [String : Codable.Type]()
modelNameToType = ["ContactModel": ContactModel.Self, "AnythingModel" : AnythingModel.Self, ...]

无论我在应用程序上创建什么,我都可以像这样成功编码并存储在缓存中:

let contact = ContactModel(name: "John")
let data = try! encoder.encode(contact)
CRUD.shared.storekey(key: "ContactModel$10", contact)

我想这样解码:

let result = try! decoder.decode(modelNameToType["ContactModel"]!, from: data)

但是我得到了错误:

Cannot invoke 'decode' with an argument list of type (Codable.Type, from: Data)

我做错了什么?感谢任何帮助

修复类型可以解决任何本地请求,但不能解决远程请求。

let result = try! decoder.decode(ContactModel.self, from: data)

联系方式:

struct ContactModel: Codable {
var name : String
}

对于远程请求,我会有这样的函数:

    func buildAnswer(keys: [String]) -> Data {

var result = [String:Codable]()
for key in keys {
let data = CRUD.shared.restoreKey(key: key)
let item = try decoder.decode(modelNameToType[key]!, from: data)
result[key] = item
}
return try encoder.encode(result)
}

...如果我解决了解码问题。任何帮助表示赞赏。

最佳答案

Codable API 是围绕从具体 类型进行编码和解码而构建的。但是,您在这里想要的往返不必知道任何具体类型;它只是将异构 JSON 值连接到一个 JSON 对象中。

因此,在这种情况下,JSONSerialization 是一个更好的工具,因为它处理 Any:

import Foundation

// I would consider lifting your String keys into their own type btw.
func buildAnswer(keys: [String]) throws -> Data {

var result = [String: Any](minimumCapacity: keys.count)

for key in keys {
let data = CRUD.shared.restoreKey(key: key)
result[key] = try JSONSerialization.jsonObject(with: data)
}
return try JSONSerialization.data(withJSONObject: result)
}

话虽这么说,您可以仍然使用 JSONDecoder/JSONEncoder 实现这一点——但是它需要相当多的类型删除样板。

例如,我们需要一个符合Encodable的包装器类型,如Encodable doesn't conform to itself :

import Foundation

struct AnyCodable : Encodable {

private let _encode: (Encoder) throws -> Void

let base: Codable
let codableType: AnyCodableType

init<Base : Codable>(_ base: Base) {
self.base = base
self._encode = {
var container = $0.singleValueContainer()
try container.encode(base)
}
self.codableType = AnyCodableType(type(of: base))
}

func encode(to encoder: Encoder) throws {
try _encode(encoder)
}
}

我们还需要一个包装器来捕获可用于解码的具体类型:

struct AnyCodableType {

private let _decodeJSON: (JSONDecoder, Data) throws -> AnyCodable
// repeat for other decoders...
// (unfortunately I don't believe there's an easy way to make this generic)
//

let base: Codable.Type

init<Base : Codable>(_ base: Base.Type) {
self.base = base
self._decodeJSON = { decoder, data in
AnyCodable(try decoder.decode(base, from: data))
}
}

func decode(from decoder: JSONDecoder, data: Data) throws -> AnyCodable {
return try _decodeJSON(decoder, data)
}
}

我们不能简单地将 Decodable.Type 传递给 JSONDecoder

func decode<T : Decodable>(_ type: T.Type, from data: Data) throws -> T

T 是协议(protocol)类型时,type: 参数采用 .Protocol 元类型,而不是 .Type 元类型(有关更多信息,请参阅 this Q&A)。

我们现在可以为我们的键定义一个类型,带有一个返回 AnyCodableTypemodelType 属性,我们可以使用它来解码 JSON:

enum ModelName : String {

case contactModel = "ContactModel"
case anythingModel = "AnythingModel"

var modelType: AnyCodableType {
switch self {
case .contactModel:
return AnyCodableType(ContactModel.self)
case .anythingModel:
return AnyCodableType(AnythingModel.self)
}
}
}

然后为往返做这样的事情:

func buildAnswer(keys: [ModelName]) throws -> Data {

let decoder = JSONDecoder()
let encoder = JSONEncoder()

var result = [String: AnyCodable](minimumCapacity: keys.count)

for key in keys {
let rawValue = key.rawValue
let data = CRUD.shared.restoreKey(key: rawValue)
result[rawValue] = try key.modelType.decode(from: decoder, data: data)
}
return try encoder.encode(result)
}

这可能设计得更好 Codable 一起工作而不是反对它(也许是一个结构来表示您发送到服务器的 JSON 对象,并使用关键路径与缓存层交互),但对 CRUD.shared 以及如何使用它一无所知;很难说。

关于json - 如何将 Swift JSONDecode 与动态类型一起使用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47472197/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com