使用Codable解码类的动态值

问题描述 投票:0回答:1

我有以下课程:

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

swift algolia codable
1个回答
0
投票

您可以处理init(来自解码器: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 }
© www.soinside.com 2019 - 2024. All rights reserved.