swift - 使用 Codable 解码类的动态值
问题描述
我有以下课程:
class AlgoliaLocation: Codable {
var id: String
var address: String?
var otherInfo: String?
}
struct AlgoliaHit<T: AlgoliaLocation>: Codable {
var highlightResult: [T.CodingKeys : [AlgoliaHighlightResult]]
var coordintates: [AlgoliaCoordinate]
enum CodingKeys: String, CodingKey {
case highlightResult = "_highlightResult"
case coordinates = "_geoloc"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let temp = try? container.decode([T.CodingKeys : AlgoliaHighlightResult].self,
forKey: .highlightResult) {
var highlightResult = [T.CodingKeys : [AlgoliaHighlightResult]]()
for (key, value) in temp {
highlightResult[key] = [value]
}
self.highlightResult = highlightResult
} else {
highlightResult = try container.decode([ T.CodingKeys : [AlgoliaHighlightResult]].self,
forKey: .highlightResult)
}
}
我在解码 的值时遇到了困难,highlightResult
因为编码键的值可以是AlgoliaHit
类模型中定义的数组,也可以是直接类型的对象AlgoliaHighlightResult
。因此,来自的每个键AlgoliaLocation.CodingKeys
都可以是类型[AlgoliaHighlightResult]
,或者AlgoliaHighlightResult
我需要一种方法在解码时循环遍历每个动态键,并在它不是数组时将值映射到数组。我试图将所有都解码为数组值和对象值,但它们是交替的,键可以是其中之一(数组或对象)。谢谢!如果不清楚,这就是我要映射的内容:
Algolia JSON。
解决方案
你可以处理它 init(from decoder: Decoder) 方法
if let objHits = try values.decodeIfPresent(Hits.self, forKey: .hits) {
hits = [objHits]
} else {
hits = try values.decodeIfPresent([Hits].self, forKey: .hits)
}
我将按照下面的代码片段正确解析它。
import Foundation
struct algolia : Codable {
let hits : [Hits]?
let page : Int?
let nbHits : Int?
let nbPages : Int?
let hitsPerPage : Int?
let processingTimeMS : Int?
let query : String?
let params : String?
enum CodingKeys: String, CodingKey {
case hits = "hits"
case page = "page"
case nbHits = "nbHits"
case nbPages = "nbPages"
case hitsPerPage = "hitsPerPage"
case processingTimeMS = "processingTimeMS"
case query = "query"
case params = "params"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
if let objHits = try values.decodeIfPresent(Hits.self, forKey: .hits) {
hits = [objHits]
} else {
hits = try values.decodeIfPresent([Hits].self, forKey: .hits)
}
page = try values.decodeIfPresent(Int.self, forKey: .page)
nbHits = try values.decodeIfPresent(Int.self, forKey: .nbHits)
nbPages = try values.decodeIfPresent(Int.self, forKey: .nbPages)
hitsPerPage = try values.decodeIfPresent(Int.self, forKey: .hitsPerPage)
processingTimeMS = try values.decodeIfPresent(Int.self, forKey: .processingTimeMS)
query = try values.decodeIfPresent(String.self, forKey: .query)
params = try values.decodeIfPresent(String.self, forKey: .params)
}}
struct Hits : Codable {
let firstname : String?
let lastname : String?
let objectID : String?
let _highlightResult : _highlightResult?
enum CodingKeys: String, CodingKey {
case firstname = "firstname"
case lastname = "lastname"
case objectID = "objectID"
case _highlightResult = "_highlightResult"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
firstname = try values.decodeIfPresent(String.self, forKey: .firstname)
lastname = try values.decodeIfPresent(String.self, forKey: .lastname)
objectID = try values.decodeIfPresent(String.self, forKey: .objectID)
_highlightResult = try values.decodeIfPresent(_highlightResult.self, forKey: ._highlightResult)
}}
struct Firstname : Codable {
let value : String?
let matchLevel : String?
enum CodingKeys: String, CodingKey {
case value = "value"
case matchLevel = "matchLevel"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
value = try values.decodeIfPresent(String.self, forKey: .value)
matchLevel = try values.decodeIfPresent(String.self, forKey: .matchLevel)
}}
struct _highlightResult : Codable {
let firstname : Firstname?
let lastname : Lastname?
let company : Company?
enum CodingKeys: String, CodingKey {
case firstname = "firstname"
case lastname = "lastname"
case company = "company"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
firstname = try values.decodeIfPresent(Firstname.self, forKey: .firstname)
lastname = try values.decodeIfPresent(Lastname.self, forKey: .lastname)
company = try values.decodeIfPresent(Company.self, forKey: .company)
}}
在您的视图控制器中使用以下代码
func jsonToCodable<T: Codable>(json: [String: Any], codable: T.Type) -> T? {
let decoder = JSONDecoder()
do {
let data = try JSONSerialization.data(withJSONObject: json, options: .prettyPrinted)
let codable = try decoder.decode(codable, from: data)
return codable
} catch {
print("*/ json failed */")
print(error)
//print(error.localizedDescription)
print(json)
}
return nil
}
if let algoliaObject = jsonToCodable(json: jsonictionary, codable: algolia.self) {// using optional chaining access _highlightResult }
推荐阅读
- delphi - 将 IHTMLDocument2 实例分配给 TWebBrowser 实例
- html - Safari 15.0 视频元素位置固定在页面加载时不起作用
- performance - QML 为大量矩形设置动画时性能缓慢
- node.js - /node_modules/angular2-moment/add.pipe.d.ts 中声明的符号 AddPipe 中的错误未从 angular2-moment/moment.module 导出
- amazon-web-services - 我可以阻止 DynamoDB 全局表的副本同步吗?
- c++ - 用于 C++ 编译的 Visual Studio Code 配置
- azure - Azure ML 发布错误 AZUREML_COMPUTE_USE COMMON_RUNTIME
- node.js - 在 AWS lambda 中对 cognito 用户进行身份验证不会调用 authenticateUser() 并继续跳过它
- python - 使用 selenium python 处理网站错误
- java - 如何使用 Mockito 模拟记录