Тип Consistency для Retrofit, RxJava2 и Room: как десериализовать JSON с той же полезной нагрузкой, но с другим «корневым ключом», если ответом является массив или объект - PullRequest
0 голосов
/ 17 февраля 2019

Я изо всех сил стараюсь поддерживать согласованные типы в моем приложении Kotlin для Retrofit, Room и RxJava2 из-за соглашения об именовании ключей JSON, которое использует API.Я попытался найти лучший способ решения проблемы ниже, но я не могу придумать лучшего способа ее решить, и мне интересно, что может предложить сообщество.

Рассмотримследующий пример:

Я использую язык Kotlin на Android и выполняю сетевые запросы с помощью Retrofit и GSON.Используемый мной API предоставляет JSON другое имя корневого ключа в зависимости от того, является ли ответ Array или единственным Object.

. Полезная нагрузка каждого Object одинакова, но еслиответом является Array, корневой ключ - множественное число.

Например, допустим, у меня есть класс данных, определенный для пользователя:

data class User(var id: Int?, name: String?)

Если я сделаю запрос APIдля одного пользователя API возвращает следующее:

{
  "user": {
    "id": 1,
    "name: "Sam"
  }
}

Если я делаю запрос API для нескольких пользователей, API возвращает следующее:

{
  "users": [{
    "id": 1,
    "name: "Sam"
  }]
}

Полезная нагрузка одинакова, но корневой ключ изменяется между user и users.Я создал класс данных для обработки отдельных корневых ключей:

data class Users(
        @SerializedName("users") var users: List<User>?,
        @SerializedName("user") var user: User?
)

И при выполнении отдельных запросов API я использую класс данных Users для обоих типов:

/** GET single user by ID */
@GET("users/{id}")
    fun getUser(
        @Path("id") id: Int?): Single<Users>

/** GET list of users */
@GET("users")
    fun getUsers(): Single<Users>

И когда я сохраняю ответ в базе данных Room, я получаю к нему доступ, используя response.users или response.user соответственно.Комната настроена на возврат запросов как User или List<User>, и это создает проблему при использовании запросов на модификацию в потоке RxJava2.Например, это очень простой поток, если база данных Room возвращает Maybe<List<User>>:

usersRepository.getAll()
    .switchIfEmpty(api.getUsers())

Результат usersRepository.getAll() равен List<User>, но если результат из базы данных пуст, и я пытаюсьвместо отката на запрос API, RxJava2 показывает ошибку Incompatible Type, потому что запрос api.getUsers() возвращает тип Single<Users>, а не Single<List<User>>.Если бы запрос API мог использовать тот же тип, у меня не было бы проблемы, но я не могу указать Single<List<User>> в запросе на модификацию, потому что GSON не может десериализовать ответ правильно.

Из-за этого, исходя из моего текущего понимания вопроса (который может быть неспециалистом, и я готов к формированию), я разделяю запросы к базе данных и API и выполняю много empty / null проверок, что мешает мне хранить все в одном хорошем и аккуратном потоке RxJava2.

Надеюсь, этот вопрос достаточно ясен, и я ценю чей-либо вклад.Спасибо за ваше время!

1 Ответ

0 голосов
/ 19 февраля 2019

Это очень распространенная проблема, и людям нравится создавать класс-обертку, который обрабатывает преобразования, проверки и все другие болезненные задания, и использовать этот класс для сетевых запросов вместо непосредственного вызова модифицированного API.

Например, рассмотрите возможность создания такого класса:

class UserClient {

    private val userApi: UserApi

    fun getUser(id: Int): Single<User> {
        return userApi.getUser(id) // This has a type of Single<Users>
            .map { it.user } // now it's map to Single<User>
    }

    fun getUsers(): Single<List<User>> {
        return userApi.getUsers() // This has a type of Single<Users>
            .doOnSuccess { Log.d(TAG, "yay!")}
            .doOnError { e -> Log.d(TAG, "Something went wrong: $e")}
            .map { it.users } // Now it's map to Single<List<User>>
    }
}

Затем вы можете сделать что-то вроде этого:

usersRepository.getAll()
    .switchIfEmpty(UserClient.getInstance().getUsers())
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...