Dizionario Swift Codable

Sep 10 2020

Sto riscontrando un problema nell'attivazione del codable. Qualsiasi aiuto sarebbe molto apprezzato. Ho quanto segue nel mio parco giochi

Esempio dal mio file JSON. Ha molti più elementi, ridotto a un sottoinsieme più piccolo.

{
  "metadata" : {
    "generated" : {
      "timestamp" : 1549331723,
      "date" : "2019-02-04 20:55:23"
    }
  },
  "data" : {
    "CA" : {
      "country-id" : 25000,
      "country-iso" : "CA",
      "country-eng" : "Canada",
      "country-fra" : "Canada",
      "date-published" : {
        "timestamp" : 1544561785,
        "date" : "2018-12-11 15:56:25",
        "asp" : "2018-12-11T15:56:25.4141468-05:00"
      }
    },
    "BM" : {
      "country-id" : 31000,
      "country-iso" : "BM",
      "country-eng" : "Bermuda",
      "country-fra" : "Bermudes",
      "date-published" : {
        "timestamp" : 1547226095,
        "date" : "2019-01-11 12:01:35",
        "asp" : "2019-01-11T12:01:35.4748399-05:00"
      }
    }
  }
}

Dall'app quicktype. Ha generato un dizionario per Datum. Il modo in cui è strutturato il json, l'abbreviazione del paese non ha un tag.

import Foundation

// MARK: - Welcome
struct Welcome: Codable {
    let metadata: Metadata?
    let data: [String: Datum]?
}

// MARK: - Datum
struct Datum: Codable {
    let countryID: Int?
    let countryISO, countryEng, countryFra: String?
    let datePublished: DatePublished?

    enum CodingKeys: String, CodingKey {
        case countryID = "country-id"
        case countryISO = "country-iso"
        case countryEng = "country-eng"
        case countryFra = "country-fra"
        case datePublished = "date-published"
    }
}

// MARK: - DatePublished
struct DatePublished: Codable {
    var timestamp: Int
    var date, asp: String
}

// MARK: - Metadata
struct Metadata: Codable {
    var generated: Generated
}

// MARK: - Generated
struct Generated: Codable {
    var timestamp: Int
    var date: String
}

// MARK: - Encode/decode helpers

class JSONNull: Codable, Hashable {

    public static func == (lhs: JSONNull, rhs: JSONNull) -> Bool {
        return true
    }

    public var hashValue: Int {
        return 0
    }

    public func hash(into hasher: inout Hasher) {
        // No-op
    }

    public init() {}

    public required init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if !container.decodeNil() {
            throw DecodingError.typeMismatch(JSONNull.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for JSONNull"))
        }
    }

    public func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encodeNil()
    }
}

Dal mio codice, posso caricare il file json, non sono sicuro di come elaborare i dati qui con il dizionario e il paese non ha un nome per l'abbreviazione del paese.

 guard let url = Bundle.main.url(forResource: "data", withExtension: "json") else { return 0 }

        let jsonData = try Data(contentsOf: url)

Nota: questo è un seguito alla mia domanda precedente: Swift Codable Parsing keyNotFound

Risposte

1 DávidPásztor Sep 09 2020 at 23:41

I tuoi modelli di dati sono già definiti correttamente (tuttavia, suggerirei alcune modifiche al nome e la rimozione di mutabilità / opzionalità dalle proprietà).

Dopo aver analizzato il JSON, non è necessario mantenere il Dictionary, poiché le chiavi fanno effettivamente parte del valore sotto la country-isochiave.

Quindi, una volta decodificato il tuo Rootoggetto, ti suggerirei semplicemente di tenerlo root.data.values, che ti dà Array<CountryData>, che puoi gestire facilmente in seguito.

struct Root: Codable {
    let data: [String: CountryData]
}

struct CountryData: Codable {
    let countryID: Int
    let countryISO, countryEng, countryFra: String
    let datePublished: DatePublished

    enum CodingKeys: String, CodingKey {
        case countryID = "country-id"
        case countryISO = "country-iso"
        case countryEng = "country-eng"
        case countryFra = "country-fra"
        case datePublished = "date-published"
    }
}

// MARK: - DatePublished
struct DatePublished: Codable {
    let timestamp: Int
    let date, asp: String
}

do {
    let root = try JSONDecoder().decode(Root.self, from: countryJson.data(using: .utf8)!)
    let countries = root.data.values
    print(countries)
} catch {
    error
}