首页 > 解决方案 > 解码 JSON 键数组

问题描述

我有这个JSON数据:

{
  "cities": [
    {
      "id": 1,
      "name": "Paris",
      "country_code": "FR",
      "attractions": [
        "CityHall",
        "Theatre",
        "Port"
      ],
      "population": 0
    },
    {
      "id": 2,
      "name": "Nice",
      "country_code": "FR",
      "attractions": [
        "CityHall"
      ],
      "population": 0
    },
    {
      "id": 3,
      "name": "Berlin",
      "country_code": "DE",
      "attractions": [
        "Theatre",
        "Port"
      ],
      "population": 0
    },
    {
      "id": 4,
      "name": "Munich",
      "country_code": "DE",
      "attractions": [
        "Theatre"
      ],
      "population": 0
    },
    {
      "id": 5,
      "name": "Amsterdam",
      "country_code": "NL",
      "attractions": [
        "Theatre",
        "Port"
      ],
      "population": 0
    },
    {
      "id": 6,
      "name": "Leiden",
      "country_code": "NL",
      "attractions": [
        "CItyHall",
        "Theatre"
      ],
      "population": 0
    }
  ]
}

我正在.request(.getCities).map([City].self, atKeyPath: "cities")使用对象使用 Moya 对其进行解码:

struct City {
  let id: Int
  let name: String
  let countryCode: String
  //let attractions: [Attraction]
  let population: Int
}

extension City: Decodable  {
  enum CodingKeys: String, CodingKey {
    case id = "id"
    case name = "name"
    case countryCode = "countryCode"
    //case attractions = "attractions"
    case population = "population"
  }
  
  init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    id = try container.decode(Int.self, forKey: .id)
    name = try container.decode(String.self, forKey: .name)
    countryCode = try container.decode(String.self, forKey: .countryCode)
    //attractions = try container.decode([Attraction].self, forKey: .attractions)
    population = try container.decode(Int.self, forKey: .population)
  }
}

简单而漂亮,但问题是我无法弄清楚如何将该attractions数组放入此处。我有它enum并试图通过使用来获取密钥codingPath

enum Attraction: String {
    case CityHall
    case Theatre
    case Port
}

extension Attraction: Decodable  {
  enum CodingKeys: String, CodingKey {

    // confusion mode initiated :) do I use it the same as in struct here?
    case cityHall = "CityHall"
    case theatre = "Theatre"
    case port = "Port"
  }
  
  init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    
    let attraction = container.codingPath.first!.stringValue // that would get the first key into string but how to deal with enum?
  }
}

另外,如果解码得attractions很好,City对象会解码嵌套的对象/数组吗?

标签: jsonswiftdecodemoya

解决方案


简单漂亮

不,简单漂亮是

struct Root : Decodable {
    let cities : [City]
}

struct City : Decodable {
  let id: Int
  let name: String
  let countryCode: String
  let atractions: [Attraction] // note the misspelling
  let population: Int
}

enum Attraction: String, Decodable {
    case cityHall = "CityHall", theatre = "Theatre", port = "Port"
}

let data = Data(jsonString.utf8)

do {
    let decoder = JSONDecoder()
    decoder.keyDecodingStrategy = .convertFromSnakeCase
    let result = try decoder.decode(Root.self, from: data)
    print(result.cities)
} catch {
    print(error)
}

推荐阅读