为一个结构解码多个 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)
}
}
}
我想解码两个 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)
}
}
}