Правильный способ разбора json для изменяемой структуры с помощью gson - PullRequest
0 голосов
/ 27 мая 2019

Я получаю ответ от сервера на основе такой структуры:

{
    "success":true,
    "data":{"can be some kind of data, array or error message"}
}

Как правильно сопоставить атрибут данных в таких ситуациях? Мои попытки заключались в использовании любого типа и приведении к указанному типу:

data class GeneralResponseModel(
    val success: Boolean,
    val data: Any
)

Средний

//
val response = gson.fromJson(it[0].toString(), GeneralResponseModel::class.java)
//

ViewModel

////////
if (res.success) {
    isLoading.postValue(false)
    ///////
} else {
    val result = res.data as ResponseError
    errorMessage.postValue(ErrorWrapper(ErrorType.REQUEST_ERROR,result.detail,result.title))
    isLoading.postValue(false)
}
///////////

И я получил

io.reactivex.exceptions.OnErrorNotImplementedException: com.google.gson.internal.LinkedTreeMap не может быть приведен к com.myapp.model.response.ResponseError

Другая попытка заключалась в использовании пустого интерфейса, который был реализован всеми возможными типами ответов. В этой ситуации я получил

java.lang.RuntimeException: невозможно вызвать конструктор без аргументов для интерфейс com.myapp.model.response.Response. Регистрация InstanceCreator в Gson для этого типа может исправить это проблема.

Я не уверен, как правильно обращаться с таким тривиальным делом. Любые ссылки, примеры кода или помощь приветствуется. Заранее спасибо.

Обновление

Благодаря Никласу я пересмотрел gson на такую ​​структуру:

 lateinit var gson: Gson
        when (methodName) {
            RequestList.LOGIN.methodName -> {
                gson =
                    GsonBuilder().registerTypeAdapter(
                        GeneralResponseModel::class.java,
                        object : JsonDeserializer<GeneralResponseModel> {
                            override fun deserialize(
                                json: JsonElement?,
                                typeOfT: Type?,
                                context: JsonDeserializationContext?
                            ): GeneralResponseModel {
                                val gsonInner = Gson()
                                val jsonObject: JsonObject = json!!.asJsonObject
                                lateinit var generalResponseModel: GeneralResponseModel
                                generalResponseModel = if (!jsonObject.get("success").asBoolean) {

                                    GeneralResponseModel(
                                        false,
                                        gsonInner.fromJson(jsonObject.get("data"), ResponseError::class.java)
                                    )
                                } else {

                                    GeneralResponseModel(
                                        true,
                                        gsonInner.fromJson(jsonObject.get("data"), DriverData::class.java)
                                    )
                                }
                                return generalResponseModel
                            }

                        }).create()
            }
            RequestList.GET_JOBS.methodName -> {
                gson = GsonBuilder().registerTypeAdapter(
                    GeneralResponseModel::class.java,
                    object : JsonDeserializer<GeneralResponseModel> {
                        override fun deserialize(
                            json: JsonElement?,
                            typeOfT: Type?,
                            context: JsonDeserializationContext?
                        ): GeneralResponseModel {
                            val gsonInner = Gson()
                            val jsonObject: JsonObject = json!!.asJsonObject
                            lateinit var generalResponseModel: GeneralResponseModel
                            generalResponseModel = if (!jsonObject.get("success").asBoolean) {

                                GeneralResponseModel(
                                    false,
                                    gsonInner.fromJson(jsonObject.get("data"), ResponseError::class.java)
                                )
                            } else {

                                GeneralResponseModel(
                                    true,
                                    gsonInner.fromJson(jsonObject.get("data"), Array<JobResponse>::class.java)
                                )
                            }
                            return generalResponseModel
                        }

                    }).create()
            }
            else -> gson = Gson()
        }

1 Ответ

1 голос
/ 27 мая 2019

Поскольку ваши данные очень обобщенные, их невозможно проанализировать безопасным для типов способом.Gson не может вывести типы на основе чистого текста (в вашем конкретном случае Gson ничего не говорит о том, что ваши данные - это ResponseError).

Я бы рассмотрел общий класс-оболочку, такой как ваш, и затем использовал бы GSON TypeAdapter для анализа ответа на вашу общую оболочку.

Вам нужно создать экземпляр GSON с помощью Builder, чтобы определить пользовательский TypeAdapter.

registerTypeAdapter(Type type, Object typeAdapter) Конфигурирует Gson длянастраиваемая сериализация или десериализация.

Ваша оболочка:

public class Response<T> {
    T data;
    String message;

    public Response(T data, String message) {
        this.data = data;
        this.message = message;
    }

    boolean hasData() {
        return data != null;
    }

    T getData() {
        return data;
    }

    String getMessage() {
        return message;
    }

}

Инициализация:

GsonBuilder builder = new GsonBuilder();
b.registerTypeAdapter(Response.class, new JsonDeserializer<Response>() {
    @Override
    public Response deserialize(JsonElement arg0, Type arg1,
        JsonDeserializationContext arg2) throws JsonParseException {
    // ... create Response object here
    return response;
}
...