Невозможно присвоить значение типа 'MRData' типу '[F1Data]' при попытке проанализировать JSON - PullRequest
0 голосов
/ 20 марта 2019

Я боролся с этим некоторое время.Я пытаюсь разобрать JSON Api в UITableview.URL-адрес API Формулы-1 .Я использую Codable, а не сторонние модули.Мысль это может сократить количество кода.Хотя, поскольку 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)).Любая помощь будет оценена с этим.

1 Ответ

0 голосов
/ 22 марта 2019

Как правильно сказал Вадиан, вам нужно расшифровать в корне ваш объект. Я кратко посмотрел это видео и пенни упала! Назначьте декодер переменной и добавьте для декодирования всю структуру, начиная с корневого объекта.

guard let data = data else { return }
            do {
                let decoder = JSONDecoder()
                // Swift 4.1
                decoder.keyDecodingStrategy = .convertFromSnakeCase
                let firstDriver = try decoder.decode(F1Data.self, from: data)
                self.champions = firstDriver.mrData.standingsTable.standingsLists[0].driverStandings
                self.tableView.reloadData()
...