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

时间:2018-06-21 04:30:48

标签: json url swift4 decode urldecode

我想对同一个项目的两个具有不同字段的JSON URL进行解码,然后使用结构或类将它们组合成一个模型对象。我能够使用一个JSON URL并根据需要对其进行解码,但是在添加第二个URL时却很费力。我目前一直在使用结构,但是我将处理大量数据,因此我知道在这里使用类会更好。任何其他建议将不胜感激。我已经搜索了很多,但是无法迅速找到与这种情况相关的信息。

下面有一个示例,希望可以对其进行操作以执行我要尝试执行的操作。在这里,我们有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)!
    }
   }
 }
}

让我们说urlStates1的JSON响应如下

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

并且urlStates2的JSON响应如下

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

1 个答案:

答案 0 :(得分:0)

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

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

2)您的if语句在解码器中始终为true,因为这两种响应都存在key .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)
    }
   }
}