Расшифруйте значение Dynami c класса, используя Codable - PullRequest
0 голосов
/ 16 марта 2020

У меня есть следующие классы:

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, и мне нужен способ прохождения l oop через каждый динамический ключ c при декодировании и сопоставления значения с массивом, когда он не массив. Я пытался декодировать все как значения массива и все как значения объекта, но они чередуются, и ключ может быть одним из них (массив или объект). Спасибо! На случай, если что-то непонятно, я пытаюсь отобразить это: Алголия JSON.

Ответы [ 2 ]

1 голос
/ 16 марта 2020

Вы можете обрабатывать его методами 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 }
0 голосов
/ 16 марта 2020

Так что мне удалось выполнить sh, используя вложенный контейнер, который я мог бы использовать для итерации динамических c ключей и проверки каждого типа:

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    let highlightResultContainer = try container.nestedContainer(keyedBy: T.CodingKeys.self, forKey: .highlightResult)

    var highlightResult = [T.CodingKeys : [AlgoliaHighlightResult]]()

    for highlightResultKey in highlightResultContainer.allKeys {
        if let value = try? highlightResultContainer.decode(AlgoliaHighlightResult.self,
                                                            forKey: highlightResultKey) {
            highlightResult[highlightResultKey] = [value]
        } else {
            let value = try highlightResultContainer.decode([AlgoliaHighlightResult].self,
                                                            forKey: highlightResultKey)
            highlightResult[highlightResultKey] = value
        }
    }
    self.highlightResult = highlightResult
}
...