Swift Codable Dictionary

Sep 10 2020

Ich habe ein Problem damit, codierbar zu werden. Jede Hilfe wäre sehr dankbar. Ich habe folgendes auf meinem Spielplatz

Beispiel aus meiner JSON-Datei. Es hat viel mehr Elemente, reduziert es eine kleinere Teilmenge.

{
  "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"
      }
    }
  }
}

Aus der Quicktype-App. Es wurde ein Wörterbuch für Datum generiert. In der Art und Weise, wie der JSON strukturiert ist, hat die Länderabkürzung kein 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()
    }
}

Aus meinem Code kann ich die JSON-Datei laden. Ich bin mir nicht sicher, wie ich die Daten hier mit dem Wörterbuch verarbeiten soll, und das Land hat keinen Namen für die Länderabkürzung.

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

        let jsonData = try Data(contentsOf: url)

Hinweis: Dies ist eine Fortsetzung meiner früheren Frage: Swift Codable Parsing keyNotFound

Antworten

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

Ihre Datenmodelle sind bereits korrekt definiert (ich würde jedoch einige Namensänderungen vorschlagen und die Veränderbarkeit / Optionalität aus den Eigenschaften entfernen).

Sobald Sie den JSON analysiert haben, müssen Sie den nicht mehr behalten Dictionary, da die Schlüssel tatsächlich Teil des Werts unter dem country-isoSchlüssel sind.

Sobald Sie Ihr RootObjekt entschlüsselt haben , würde ich vorschlagen, es einfach zu behalten root.data.values, was Ihnen gibt Array<CountryData>, was Sie anschließend problemlos handhaben können.

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
}