Декодированные поля JSON дают только нулевые значения, не могут быть сохранены в переменные - PullRequest
0 голосов
/ 16 января 2019

Вот JSON, который мне нужно декодировать, его изображение доступно по ссылке ниже, а также текст (форматирование не сработает, поэтому выглядит уродливо). Я почти уверен, что правильно представил его со своими структурами.

enter image description here

{ "успех": { "всего": 1 }, "содержание": { "цитаты": [ { "цитата": "В прошлый раз не существует. Это только в этот раз. И в этот раз все будет иначе. Там только сейчас"., «автор»: «Билл Мюррей», «длина»: «118», "теги": [ «Вдохновлять», "подарок" ], "категория": "вдохновлять", "title": "Вдохновляющая цитата дня", «дата»: «2019-01-16», "id": null } ], "copyright": "2017-19 theysaidso.com" } }

Всякий раз, когда я запускаю свой код, который пытается взять поля из JSON и сохранить их в переменных, чтобы отобразить их в UITableView, происходит сбой. Я проверяю это, пытаясь сделать ярлык таким же, как имя автора в качестве заголовка. «автор» - это поле в JSON. Это важные части кода:

Class ViewController: UITableViewController  {

...
var quoteArray = [Quote]()
//quoteArray Stores the quote objects that contain the fields I need
.....
//STRUCTS TO REPRESENT THE JSON
struct Quote: Decodable {
    let quote: String?
    let author: String?
    let length: String?
    let tags: [String]?
    let category: String?
    let title: String?
    let date: String?
}

struct WebsiteObjectStruct: Decodable {
    let success: SuccessStruct
    let contents: ContentsStruct
}

struct SuccessStruct: Decodable{
    let total: Int?
}

struct ContentsStruct: Decodable{
    let quotes: [Quote]?
    let copyright:  String?
}

.....

// ФУНКЦИЯ, ГДЕ ДЕКОДИРУЕТСЯ

fileprivate func fetchJSON(){
        ...
        self.websiteObject = try decoder.decode(WebsiteObjectStruct.self, from: data)
        self.tableView.reloadData()
        ...
}
...

// Ячейка для строки в функции для таблицы

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "cellId")
    let authorText = quoteArray[0].author
    cell.textLabel?.text = author.text
    //For quoteArray we are looking at zero index because in my JSON there
    // is ONLY EVER ONE element, located at index 0, in quoteArray
    return cell
}

}

Приложение запускается, а tableView пуст, у него нет имени автора (в данном случае Билла Мюррея). В любом случае, вот сообщение об ошибке:

Не удалось декодировать: typeMismatch (Swift.Array, Swift.DecodingError.Context (codingPath: [], debugDescription: Msgstr "Предполагается декодировать массив, но вместо этого нашел словарь.", ringError: nil))

Он говорит, что ожидал декодировать массив, но вместо этого нашел словарь. Ну, я изменил его один раз, чтобы декодировать не массив, а структуру, и объявил переменную в классе, который имел тип struct (назначение структуры отражает назначение массива).

Короче говоря, я немного изменил код, чтобы приспособить его к структуре, и он мог бы печатать имя автора ТОЛЬКО на консоли, если оператор печати находился в тех же скобках кодирования, что и оператор декодирования. Тем не менее, он не мог сохранить его в переменной для использования.

Я не думаю, что проблема в массиве по сравнению со словарем , но с "basicError" консоль говорит о том, что массив равен nil . Независимо от типа переменной, будь то массив или структура, переменная, помещаемая в textField, всегда равна нулю.

Я получаю эту ошибку:

Неустранимая ошибка: неожиданно найдено ноль при развертывании необязательного значения

Возможно, проблема с многопоточностью или асинхронностью?

Обновление: этот код работает:

class MainNetworkManager { // Запросить формат JSON из wep API

static func fetchJSON(fetchUrl: String, quoteViewController: QuoteViewController) {
    let urlString = fetchUrl
    guard let url = URL(string: urlString) else { return }
    URLSession.shared.dataTask(with: url) { (data, _, 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 {
                // link in description for video on JSONDecoder
                let decoder = JSONDecoder()
                // Swift 4.1
                decoder.keyDecodingStrategy = .convertFromSnakeCase
                //self.webStruct = try decoder.decode(WebsiteObjectStruct.self, from: data)
                //                        self?.quoteArray = quotesArray
                //                        self?.reloadInputViews()
                let tempStruct = try decoder.decode(WebsiteObjectStruct.self, from: data)

                //print(tempStruct.contents.quotes[0].length)
                quoteViewController.webStruct = tempStruct

                //quoteViewController.setupLabels(array: (tempStruct.contents.quotes))
                quoteViewController.setupLabels(obj: tempStruct)


            } catch let jsonErr {
                print("Failed to decode:", jsonErr)
            }
        }
        }.resume()
}

Ответы [ 2 ]

0 голосов
/ 16 января 2019

Два выпуска.

  1. Для использования quoteArray необходимо скопировать массив, содержащий кавычки

    self.websiteObject = try decoder.decode(WebsiteObjectStruct.self, from: data)
    self.quoteArray = self.websiteObject.contents.quotes ?? []
    DispatchQueue.main.async {
        self.tableView.reloadData()
    }
    
  2. В cellForRow вы должны получить предмет по указанному пути индекса. И снять ячейку, установить стиль в Интерфейсном Разработчике.

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
        let cell = tableview.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath)
        let quote = quoteArray[indexPath.row]
        cell.textLabel?.text = quote.author
        cell.detailTextLabel?.text = quote.quote
        return cell
    }
    
0 голосов
/ 16 января 2019

Немного трудно понять ошибку с кодом, который вы разместили. Я думаю, что проблема может быть с определением типа переменной websiteObject. Я создал небольшую игровую площадку, чтобы проверить ее, и у тебя все хорошо.

Я создаю небольшой проект, который работает нормально, используя ваши структуры. Вы можете проверить это здесь: https://github.com/acyrman/StackOverflow54211226.

Соответствующее изменение в функции fetchJSON. Вместо использования self.websiteObject, который я не знал, как вы его определили, я использовал локальную переменную, подобную этой: let websiteObject = try decoder.decode(WebsiteObjectStruct.self, from: data), затем приступил к получению кавычек и присвоению вашей переменной quoteArray.

fileprivate func fetchJSON() {
    let urlString = "http://quotes.rest/qod.json?category=inspire"
    guard let url = URL(string: urlString) else { return }
    URLSession.shared.dataTask(with: url) { [weak self] (data, response, error) in
        if error != nil {
            self?.displayAlert("Error fetching data: \(String(describing: error?.localizedDescription))")
        }

        let decoder = JSONDecoder()
        do {
            guard let data = data else { throw NSError(domain: "this.app", code: -1, userInfo: nil) }

            let websiteObject = try decoder.decode(WebsiteObjectStruct.self, from: data)
            if let quotesArray = websiteObject.contents.quotes {
                DispatchQueue.main.async {
                    self?.quoteArray = quotesArray
                    self?.tableView.reloadData()
                }
            }
        } catch let error {
            self?.displayAlert("Error decoding json data: \(String(describing: error.localizedDescription))")
        }
    }.resume()
}

Для приложения я получаю цитату из: http://quotes.rest/qod.json?category=inspire. Также в info.plist важно включить настройки ATS, чтобы включить выборку данных из URL-адреса, отличного от https.

enter image description here

Код предназначен только для проверки ваших структур, не ожидайте проекта с чистым кодом;)

Приложение вызывает fetchJSON в viewDidLoad, используя стиль ячейки subtitle, интерфейс выглядит следующим образом:

enter image description here

...