【发布时间】:2019-03-20 20:15:40
【问题描述】:
我已经为此苦苦挣扎了一段时间。我正在尝试将 JSON Api 解析为 UITableview。网址是 Formula One API 。我正在使用 Codable 而不是第三方 pod。认为这可能会减少代码量。虽然,由于 API 不是那么直接,所以很难提取我想要的东西。基本上,我想列出给定年份司机的当前排名。在我给出的 url 和代码中,我选择了 1999 作为示例。我一直在研究 Stackoverflow,但每个解决方案都针对特定问题非常具体,我似乎与我的问题无关。下面是我的代码。
struct MRData: Codable {
let xmlns: String?
let series: String?
let url: String?
let limit, offset, total: String?
let standingsTable: StandingsTable
enum CodingKeys: String, CodingKey {
case xmlns, series, url, limit, offset, total
case standingsTable = "StandingsTable"
}
}
struct StandingsTable: Codable {
let season: String?
let standingsLists: [StandingsList]
enum CodingKeys: String, CodingKey {
case season
case standingsLists = "StandingsLists"
}
}
struct StandingsList: Codable {
let season, round: String?
let driverStandings: [DriverStanding]
enum CodingKeys: String, CodingKey {
case season, round
case driverStandings = "DriverStandings"
}
}
struct DriverStanding: Codable {
let position, positionText, points, wins: String?
let driver: Driver
let constructors: [Constructor]
enum CodingKeys: String, CodingKey {
case position, positionText, points, wins
case driver = "Driver"
case constructors = "Constructors"
}
}
struct Constructor: Codable {
let constructorId: String?
let url: String?
let name: String?
let nationality: String?
}
struct Driver: Codable {
let driverId: String?
let url: String?
let givenName, familyName, dateOfBirth, nationality: String?
}
class f1TableViewController: UITableViewController {
var champions: [F1Data] = []
override func viewDidLoad() {
super.viewDidLoad()
// let jsonUrlString = "https://api.letsbuildthatapp.com/jsondecodable/website_description"
navigationController?.navigationBar.prefersLargeTitles = true
navigationItem.title = "Champion Drivers"
fetchJSON()
}
private func fetchJSON(){
let jsonUrlString = "https://ergast.com/api/f1/1999/driverstandings.json"
guard let url = URL(string: jsonUrlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
DispatchQueue.main.async {
if let err = err {
print("Failed to get data from url:", err)
return
}
guard let data = data else { return }
do {
let decoder = JSONDecoder()
// Swift 4.1
decoder.keyDecodingStrategy = .convertFromSnakeCase
self.champions = try decoder.decode(MRData.self, from: data)
self.tableView.reloadData()
//let season = f1Data.mrData.standingsTable.season
// let firstDriver = f1Data.mrData.standingsTable.standingsLists[0].driverStandings
// for driver in firstDriver {
//
// print("\(driver.driver.givenName) \(driver.driver.familyName)")
// }
//print(season)
} catch {
print(error)
}
}
}.resume()
}
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return champions.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "cellId")
let champion = champions[indexPath.row]
let driverName = champion.mrData.standingsTable.standingsLists[0].driverStandings
for driver in driverName {
cell.textLabel?.text = driver.driver.familyName
}
//cell.textLabel?.text =
//cell.detailTextLabel?.text = String(course.numberOfLessons)
return cell
}
}
现在我意识到错误在 do catch 块中。
do {
let decoder = JSONDecoder()
// Swift 4.1
decoder.keyDecodingStrategy = .convertFromSnakeCase
self.champions = try decoder.decode(MRData.self, from: data)
self.tableView.reloadData()
并且数组 F1Data 不能是 MRData 的字典。因此,如果我将其更改为以下self.champions = try decoder.decode([F1Data].self, from: data),我会收到另一个错误,即
debugDescription: "Expected to decode Array<Any> but found a dictionary instead.", underlyingError: nil))。任何帮助将不胜感激。
【问题讨论】:
-
请阅读您的代码。错误很明显。你解码
MRData并且champions被声明为[F1Data]。 JSON 层次结构中根本没有结构F1Data。 -
我添加了以下作为我的冠军变量
var champions: [DriverStanding] = []和以下作为解码器do { let decoder = JSONDecoder() // Swift 4.1 decoder.keyDecodingStrategy = .convertFromSnakeCase self.champions = try decoder.decode([DriverStanding].self, from: data) self.tableView.reloadData()我得到的错误是以下debugDescription: "Expected to decode Array<Any> but found a dictionary instead.", underlyingError: nil))请帮助。 -
您必须始终解码从根对象开始的完整结构。
-
感谢您的回复,但我知道如果我这样做,我会收到另一个错误。我稍后会尝试这个并发布错误。
标签: json swift4 codable decoder