Декодирование JSON Response с тем же конвертом, но с другим содержимым - PullRequest
1 голос
/ 04 октября 2019

У меня есть доступ к веб-интерфейсу API, и в ответ он отправляет различные полезные данные, упакованные в один и тот же конверт, например:

Получите список рецептов:

{
    "status": "SUCCESS",
    "messages": [],
    "input": null,
    "output": [
        {
            "id": 1,
            "title": "Egg with bacon"
        },
        {
            "id": 2,
            "title": "Ice cream"
        }
    ]
}

Получите один рецепт:

{
    "status": "SUCCESS",
    "messages": [],
    "input": {"id": 1},
    "output": {
        "id": 1,
        "title": "Egg with bacon"
    }
}

Ответ об ошибке:

{
    "status": "ERROR",
    "messages": ["Recipe not found"],
    "input": {"id": 4},
    "output": null
}

Список категорий:

{
    "status": "SUCCESS",
    "messages": [],
    "input": null,
    "output": [
        {
            "id": 1,
            "title": "Deserts"
        },
        {
            "id": 2,
            "title": "Main Courses"
        }
    ]
}

Таким образом, ключи конверта всегда присутствуют. Ввод - это объект значения ключа или ноль, сообщения - это всегда массив строк или пустой массив, статус - строка. Но вывод может быть разным. Это может быть тип структуры «Рецепт», массив структур «Рецепт» или структура категории.

Мой вопрос: как я могу декодировать этот json, не записывая каждый раз одну и ту же логику декодирования для конверта? Я хочу написать декодер только один раз для конверта и ввести разные декодеры для вывода.

1 Ответ

1 голос
/ 04 октября 2019

Вы можете создать декодируемую структуру, которую вы будете использовать для переноса ввода и вывода.

Это будет выглядеть так:

struct ResponseContainer<Input: Decodable, Output: Decodable>: Decodable {
    var status: String
    var messages: [String]
    var input: Input?
    var output: Output
}

Используя это, если вы хотите декодироватьодин рецепт, вы просто заключаете свою Recipe структуру, используя в контейнер ответов:

// used to decode the `input`
struct InputId: Decodable {
    var id: Int
}

// content of the `output`
struct Recipe: Decodable {
    var id: Int
    var title: String
}

try? JSONDecoder().decode(ResponseContainer<InputId, Recipe>.self, from: singleRecipeJson)

Если вы хотите декодировать список рецептов, просто идите тем же путем с другой структурой или массивом:

// As the input is expected to be null, you can use a dummy struct in the wrapper.
struct Empty: Decodable {}

try! JSONDecoder().decode(ResponseContainer<Empty, [Recipe]>.self, from: multipleRecipeJson)

Примечание: Фиктивная структура Empty может оказаться бесполезной, поскольку она добавляет много сложности, и используется для анализа свойства input полезной нагрузки, которое выглядит следующим образомчто-то, что вы отправили в API (так что, в принципе, вы это уже знаете, и это можно игнорировать). В этом случае оболочка будет выглядеть так:

struct ResponseContainer<Output: Decodable>: Decodable {
    var status: String
    var messages: [String]
    var output: Output
}
...