swift - 解码通用可解码类型
问题描述
我需要创建一个通用结构来保存从网络返回的任何可解码类型,所以我创建了这样的东西:
struct NetworkResponse<Wrapped: Decodable>: Decodable {
var result: Wrapped
}
所以我可以使用这样的解码方法:
struct MyModel: Decodable {
var id: Int
var name: String
var details: String
}
func getData<R: Decodable>(url: URL) -> AnyPublisher<R, Error>
URLSession.shared
.dataTaskPublisher(for: url)
.map(\.data)
.decode(type: NetworkResponse<R>.self, decoder: decoder)
.map(\.result)
.eraseToAnyPublisher()
//call site
let url = URL(string: "https://my/Api/Url")!
let models: [MyModel] = getData(url: url)
.sink {
//handle value here
}
但是,我注意到来自网络的一些响应包含result
密钥,而另一些则不包含:
结果:
{
"result": { [ "id": 2, "name": "some name", "details": "some details"] }
}
没有结果:
[ "id": 2, "name": "some name", "details": "some details" ]
这会导致发布者出现以下错误,因为它在返回的 json 中.map(\.result)
找不到密钥:result
(typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Dictionary<String, Any> but found an array instead.", underlyingError: nil)))
我如何处理NetworkResponse
结构中的任何一种情况以避免此类错误?
解决方案
您发布的 JSON 无效,但我假设这是一个错字,实际上是:
{ "id": 2, "name": "some name", "details": "some details" }
// or
{ "result": { "id": 2, "name": "some name", "details": "some details" } }
({
}
而不是[
]
)
可能最干净的是使用手动解码器,如果第一种类型失败,它可以回退到另一种类型:
struct NetworkResponse<Wrapped> {
let result: Wrapped
}
extension NetworkResponse: Decodable where Wrapped: Decodable {
private struct ResultResponse: Decodable {
let result: Wrapped
}
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
do {
let result = try container.decode(ResultResponse.self)
self.result = result.result
} catch DecodingError.keyNotFound, DecodingError.typeMismatch {
self.result = try container.decode(Wrapped.self)
}
}
}
或者,您可以退回到 Combine。我不会采用这种方法,但为了完整起见:
URLSession.shared
.dataTaskPublisher(for: url)
.map(\.data)
.flatMap { data in
Just(data)
.decode(type: NetworkResponse<R>.self, decoder: decoder)
.map(\.result)
.catch { _ in
Just(data)
.decode(type: R.self, decoder: decoder)
}
}
.eraseToAnyPublisher()
推荐阅读
- angular - 如何以角度进行页面缓存
- azure - 逻辑应用程序:在 Json 对象数组中查找元素(如 XPath fr XML)
- c++ - 如何在 C++ 中“退出当前函数,如果当前语句出错”
- angular - 客户端代码中的 Webpack 编译哈希
- sql-server - 数据库中所有表的系统版本控制(临时表)
- java - Fuseki 服务器不显示数据集
- python - TensorFlow Serving:将图像传递给分类器
- php - 使用 form_open 函数将 get 参数添加到 URL?
- html - 在 react spa 应用程序中有条件地应用 css 样式
- sql - Hive 简单正则表达式