为一个结构解码多个 JSON 请求

Decoding multiple JSON requests for one struct

我想解码两个 JSON url,它们对同一项目具有不同的字段,然后使用结构或 class 将它们组合成一个模型对象。我可以使用一个 JSON url 并根据需要对其进行解码,但我正在努力添加第二个 url。我目前一直在使用结构,但我将处理大量数据,所以我知道 class 在这里使用会更好。任何其他建议将不胜感激。我已经搜索了很多,但无法在 swift 4.

中找到与这种情况相关的信息

我在下面有一个例子,我希望可以操纵它来执行我正在尝试做的事情。这里我们有 1 个端点,提供状态、缩写和区域成员。另一个端点提供州、人口和面积。

class CollectionViewController: UIViewController {

let urlStates1 = "https://......endpoint1"
let urlStates2 = "https://......endpoint2"

var states = [StatesData]()

override func viewDidLoad() {
    super.viewDidLoad()

    downloadJSON() {
        self.CollectionView.reloadData()
    }
}

func downloadJSON(completed:@escaping ()->()){
    let url1 = URL(string: urlStates1)

    URLSession.shared.dataTask(with: url1!) { (data, response, error) in
        if error == nil {
            do{
                self.states = try JSONDecoder().decode([StatesData].self, from: data!)
                DispatchQueue.main.async{
                    completed()
                }
            } catch {
                print("JSON Error")
            }}
        }.resume()
}

这是我的结构

struct StatesData : Decodable {

//////Members from URL1///////

   var state : String?
   var abrev : String?
   var region : String?

//////Members specific to URL2///////

   var population : Int?
   var area : Double?

private enum AttributeKeys: String, CodingKey {
    case state = "state"
    case abrev = "abrev"
    case region = "region"
   }

 private enum StatisticsKeys: String, CodingKey {
    case state = "state"
    case population = "population"
    case area = "area"
  }

init(from decoder: Decoder) throws {
    let values = try decoder.container(keyedBy: AttributeKeys.self)
    if let stateName = try values.decodeIfPresent(String.self, forKey: .state) {
        state = stateName
        abrev = try values.decodeIfPresent(String.self, forKey: .abrev)!
          region = try values.decodeIfPresent(String.self, forKey: .region)!
    } else {
        let values = try decoder.container(keyedBy: StatisticsKeys.self)
        state = try values.decodeIfPresent(String.self, forKey: .state)!
        population = try values.decodeIfPresent(Int.self, forKey: .population)!
        area = try values.decodeIfPresent(Double.self, forKey: .area)!
    }
   }
 }
}

假设 JSON 对 urlStates1 的响应如下

[
 {
  "state": "Connecticut",
  "abrev": "CT",
  "region": "Northeast"
 },
 {
  "state": "Texas",
  "abrev": "TX",
  "region": "Southwest"
 }
]

JSON urlStates2 的响应如下

[
 {
  "state": "Connecticut",
  "population": 3588000,
  "area": 5543.00
 },
 {
  "state": "Texas",
  "population": 28300000,
  "area": 268597.00
 }
]

在您当前的实施中,您的 StatesData 结构中存在两个问题。

1) 属性 类型与响应中的值类型不匹配(即人口和面积)。

2) 您的 if 语句在解码器中始终为真,因为键 .state 存在于两种响应中,因此它永远不会解码第二种响应。

请查看更正后的 StatesData,现在您应该能够正确解码响应。

struct StatesData : Decodable {

//////Members from URL1///////

var state : String?
var abrev : String?
var region : String?

//////Members specific to URL2///////

var population : String?
var area : String?

private enum AttributeKeys: String, CodingKey {
    case state = "state"
    case abrev = "abrev"
    case region = "region"
}

private enum StatisticsKeys: String, CodingKey {
    case state = "state"
    case population = "population"
    case area = "area"
}

init(from decoder: Decoder) throws {
    let values = try decoder.container(keyedBy: AttributeKeys.self)
    if let abrevName = try values.decodeIfPresent(String.self, forKey: .abrev) {
        state = try values.decodeIfPresent(String.self, forKey: .state)
        abrev = abrevName
        region = try values.decodeIfPresent(String.self, forKey: .region)
    } else {
        let values = try decoder.container(keyedBy: StatisticsKeys.self)
        state = try values.decodeIfPresent(String.self, forKey: .state)
        population = try values.decodeIfPresent(String.self, forKey: .population)
        area = try values.decodeIfPresent(String.self, forKey: .area)
    }
   }
}