简体   繁体   中英

Decoding JSON with a dictionary with (no) keys

I'm having trouble to decode a simple JSON api result using structs.

This is the JSON stucture (part of it):

{
  "ad": "Andorra",
  "ae": "United Arab Emirates",
  "af": "Afghanistan",
  "ag": "Antigua and Barbuda",
  "ai": "Anguilla",
  "al": "Albania"
}

This are the structs I created:

struct Countries: Codable {
    var countries: [Country]
}

struct Country: Codable, Identifiable {
    var id = UUID()
    var code: String
    var name: String
}

And using an API I am doing this to try to decode it:

let decodedResponse = try JSONDecoder().decode(Countries.self, from: data)

At the moment this is the error:

No value associated with key CodingKeys(stringValue: "countries", intValue: nil) ("countries").

As I understand correctly the JSON result has two things, the keys and the values. The keys in this case are the country codes (two letters) and the values are the country names. I do want to use both of them in my app but I struggle to use both the key and value using a struct. The error at the moment is also because the dictionary itself has no key. But I can also imagine the values in a single Country will also not work.

Implementing custom encoder & decoder logic can help transform between expected json [String:String] and Countries / [Country] .

struct Countries: Codable {
  struct Country {
    let code: String
    let name: String
  }
  
  let countries: [Country]

  //custom decoder logic; from json to model
  init(from decoder: Decoder) throws {
    let container = try decoder.singleValueContainer()
    
    let dict = try container.decode([String:String].self)
    countries = dict.map(Country.init)
  }

  //custom encoder logic; from model to json
  func encode(to encoder: Encoder) throws {
    var container = encoder.singleValueContainer()

    var dict = [String:String]()
    countries.forEach { (country) in
      dict[country.code] = country.name
    }
    try container.encode(dict)
  }
}

Usage Example**:

let jsonData = """
{
  "ad": "Andorra",
  "ae": "United Arab Emirates",
  "af": "Afghanistan",
  "ag": "Antigua and Barbuda",
  "ai": "Anguilla",
  "al": "Albania"
}
""".data(using: .utf8)!

do {
  let result = try JSONDecoder().decode(Countries.self, from: jsonData)
  result.countries.forEach { print($0) }
  
  let data = try JSONEncoder().encode(result)
  let json = String(data: data, encoding: .utf8)!
  print(json)
} catch {
  print(error)
}

**ignore the force-unwraps, it's just test code

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM