Получение объектов из JSON

Моя проблема: Я использую API сайта — https://www.themealdb.com/api.php.

Я хочу получить список всех продуктов. Для этого ссылка https://www.themealdb.com/api/json/v1/1/categories.php

В моем коде я создал структуру:

struct Category: Decodable {
    var idCategory: Int?
    var strCategory: String?
    var strCategoryDescription: String?
    var strCategoryThumb: String?
}

Затем я пытаюсь добраться до адреса и получить данные. Я могу преобразовать входящие данные в JSON. Оно работает.

enter image description here

Далее я хочу преобразовать данные и записать их в массив структур.

func load(url: String, completion: @escaping (_ objects: [Category])->()) {

    guard let url = URL(string: url) else { return }

    let session = URLSession.shared

    session.dataTask(with: url) { (data, response, error) in

        guard let data = data else { return }

        do {
            //let json = try? JSONSerialization.jsonObject(with: data, options: [])
            //print("JSONSerialization" + "\(json)")

            let object = try JSONDecoder().decode([Category].self, from: data)
            print("JSONDecoder" + "\(object)")
            completion(object)

        } catch {
            print(error.localizedDescription)
        }
    }.resume()
}

Но в этой строке получаю в консоли ошибку:

The data couldn’t be read because it isn’t in the correct format.

Вероятно, ошибка в моей структуре. Я не могу справиться с этой проблемой.

print(error.localizedDescription) => print(error). Это даст вам больше объяснений по ошибке.
Larme 22.05.2019 17:15

Я бы предложил использовать обработчик завершения [Category]? или, лучше, Result<[Category], Error> типа, чтобы вы могли отличить пустой набор результатов от ошибки. Это будет особенно полезно, если API имеет возможность фильтрации/поиска.

Rob 22.05.2019 18:14
3 метода стилизации элементов HTML
3 метода стилизации элементов HTML
Когда дело доходит до применения какого-либо стиля к нашему HTML, существует три подхода: встроенный, внутренний и внешний. Предпочтительным обычно...
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.
0
2
95
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вам нужно два кода

struct MyData: Codable {
    var categories: [Category]?
}

А также

let object = try JSONDecoder().decode(MyData.self, from: data)
Ответ принят как подходящий

Есть ошибки два.

  1. Фактическая ошибка

    Type 'Array' mismatch: Expected to decode Array but found a dictionary instead.

    указывает, что вы игнорируете корневой объект, словарь с ключом categories

  2. Значение для ключа id равно String, а не Int, обратите внимание на двойные кавычки в JSON.


Объявите все элементы структуры как константы необязательный, поскольку JSON предоставляет все ключи в словарях. И, пожалуйста, сопоставьте ужасные словарные ключи с более осмысленными именами участников.

И выведите все errors и никогда.localizedDescription в блоке Decodable catch.

struct Response: Decodable {
    let categories: [Category]
}

struct Category: Decodable {
    let id: String
    let name: String
    let description: String
    let thumbnailURL: URL

    private enum CodingKeys: String, CodingKey {
        case id = "idCategory"
        case name = "strCategory"
        case description = "strCategoryDescription"
        case thumbnailURL = "strCategoryThumb"
    }
}

func load(url: String, completion: @escaping ([Category]) -> Void) {

    guard let url = URL(string: url) else { return }

    let session = URLSession.shared    
    session.dataTask(with: url) { (data, _, error) in

        if let error = error { print(error); return }

        do {
            let response = try JSONDecoder().decode(Response.self, from: data!)
            print("JSONDecoder", response)
            completion(response.categories)

        } catch {
            print(error)
            completion([])
        }
    }.resume()
}

С помощью класса-оболочки вы можете получить свои категории. Следующий код отлично работает в Playground:

let json = """
{
    "categories": [
        {"idCategory": "1"},
        {"idCategory": "2"}
    ]
}
"""

struct CategoryHolder: Codable {
    var categories: [Category]
}

struct Category: Codable {
    let idCategory: String?
    let strCategory: String?
    let strCategoryDescription: String?
    let strCategoryThumb: String?
}

let jsonData = Data(json.utf8)
let categories = try JSONDecoder().decode(CategoryHolder.self, from: jsonData).categories

Другие вопросы по теме