0

I am trying to parse the below JSON response, which has multiple dynamic keys,

{
"Nagaland": {
    "districtData": {
      "Dimapur": {
        "confirmed": 1,
        "lastupdatedtime": "",
        "delta": {
          "confirmed": 0
        }
      }
    }
  },
  "Meghalaya": {
    "districtData": {
      "East Khasi Hills": {
        "confirmed": 1,
        "lastupdatedtime": "",
        "delta": {
          "confirmed": 0
        }
      }
    }
  }
}

I have written my Codable struct like below,,

struct IndianStateListModel: Codable {
    // MARK: Properties
    let state: [String: StateData]
}

struct StateData: Codable {
    // MARK: Properties
    var districtData: Inner?

    /// Mapping Key Enum
    private enum CodingKeys: String, CodingKey {
        case districtData
    }
}

struct Inner: Codable {
    // MARK: Properties
    let districts: [String: DistrictData]
}


struct DistrictData: Codable {
    // MARK: Properties
    var confirmed: Int?
    var lastupdatedtime: String?
    var delta: DailyConfirmedData?

    /// Mapping Key Enum
    private enum CodingKeys: String, CodingKey {
        case confirmed, lastupdatedtime, delta
    }
}

struct DailyConfirmedData: Codable {
    // MARK: Properties
    var confirmed: Int?

    /// Mapping Key Enum
    private enum CodingKeys: String, CodingKey {
        case confirmed
    }
}

It's called as,

let summary = try JSONDecoder().decode(IndianStateListModel.self, from: data)

But its returning nil

P.S.: related question regarding decodable Swift Codable with dynamic keys

Any solution, would be great, Thanks in advance

Bappaditya
  • 9,494
  • 3
  • 20
  • 29

2 Answers2

3

The Codable models that you must use to parse the above JSON data should be like,

Models:

struct StateData: Codable {
    var districtData: [String:DistrictData]?
}

struct DistrictData: Codable {
    var confirmed: Int?
    var lastupdatedtime: String?
    var delta: DailyConfirmedData?
}

struct DailyConfirmedData: Codable {
    var confirmed: Int?
}

Parsing:

let summary = try JSONDecoder().decode([String:StateData].self, from: data)

Note: There is no need to explicitly create enum CodingKeys if the JSON keys exactly match the properties of the Codable type.

PGDev
  • 23,751
  • 6
  • 34
  • 88
2

The fundamental issue is that IndianStateListModel has a property called states. But no such key appears in your JSON. I’d suggest parsing it with singleValueContainer. E.g. perhaps:

struct States: Decodable {
    typealias StateName = String
    let states: [StateName: Districts]

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        states = try container.decode([StateName: Districts].self)
    }
}

struct Districts: Decodable {
    typealias DistrictName = String
    var districts: [DistrictName: DistrictData]

    enum CodingKeys: String, CodingKey {
        case districts = "districtData"
    }
}

struct DistrictData: Decodable {
    var confirmed: Int
    var lastupdatedtime: String
    var delta: DailyConfirmedData
}

struct DailyConfirmedData: Decodable {
    var confirmed: Int?
}

And

do {
    let result = try JSONDecoder().decode(States.self, from: data)
    print(result)
} catch {
    print(error)
}
Rob
  • 415,655
  • 72
  • 787
  • 1,044