Моши и Retrofit2: ожидаемый BEGIN_OBJECT, но был STRING - PullRequest
0 голосов
/ 03 февраля 2020

Я сталкиваюсь с этой проблемой в моем проекте. Я получаю от api call ответ вроде:

{
    "aResponse": {
        "listOfSomething": [
             //here some data
        ]
    }
}

И относительные классы данных

data class ResponseClass(
    val aResponse : AResponse
) 

data class AResponse(
    val listOfSomething : List<String>
)

Не бывает так, что когда listOfSomething пуст, я получаю этот ответ:

{
    "aResponse": {
        "listOfSomething": ""
    }
}

, который выдает (конечно) исключение

com.squareup.moshi.JsonDataException: Expected BEGIN_OBJECT but was STRING

Как я могу это решить?

Ответы [ 3 ]

3 голосов
/ 03 февраля 2020

Вы получаете эту ошибку, например, когда есть данные, вы получаете массив, а когда нет данных, получается строка, которая неверна при модернизации.

Если нет данных insise listOfSomething, то попросите серверную часть отправить пустую массив вместо строки.

{
    "aResponse": {
        "listOfSomething": []
    }
}

вместо

{
    "aResponse": {
        "listOfSomething": ""
    }
}
1 голос
/ 03 февраля 2020

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

Если вы не можете исправить это на стороне сервера, потому что API не находится под вашим контролем, вы можете попробовать что-то вроде этого:

public final class IgnoreStringForArrays implements JsonAdapter.Factory {

    @Retention(RetentionPolicy.RUNTIME)
    @JsonQualifier
    public @interface IgnoreJsonArrayError {
    }

    @Override
    public JsonAdapter<?> create(Type type, Set<? extends Annotation> annotations, Moshi moshi) {
        if (annotations != null && annotations.size() > 0) {
            for (Annotation annotation : annotations) {
                if (annotation instanceof IgnoreJsonArrayError) {
                    final JsonAdapter<Object> delegate = moshi.nextAdapter(this, type, Types.nextAnnotations(annotations, IgnoreJsonArrayError.class));
                    return new JsonAdapter<Object>() {
                        @Override
                        public Object fromJson(JsonReader reader) throws IOException {
                            JsonReader.Token peek = reader.peek();
                            if (peek != JsonReader.Token.BEGIN_ARRAY) {
                                reader.skipValue();
                                return null;
                            }
                            return delegate.fromJson(reader);
                        }

                        @Override
                        public void toJson(JsonWriter writer, Object value) throws IOException {
                            delegate.toJson(writer, value);
                        }
                    };
                }
            }
        }
        return null;
    }
}

как предложено здесь: https://github.com/square/moshi/issues/295#issuecomment -299636385

А затем аннотируйте ваш listOfSomething с помощью: IgnoreJsonArrayError аннотация

1 голос
/ 03 февраля 2020

Если ваш json результат изменится в зависимости от результата, в первую очередь ваш бэкэнд делает плохую работу, тогда вам придется немного "взломать" ваше приложение, чтобы адаптировать код ...

Ваш класс POJO должен быть:

data class MyResponse(
    val aResponse: AResponse
)

data class AResponse(
    val listOfSomething: Any
)

Вы можете объявить его как Any, что не является хорошей практикой, но это обходной путь, чтобы заставить его работать в соответствии с вашим бэкэндом. Это как в Java добавление Object

Тогда вы можете сделать что-то в своем onResponse

@Override
fun onResponse(response: Response<MyResponse>) {
    if (response.isSuccess()) {
        if (response.listOfSomething is String) {
            //do something with the String
        } else {
            //do something with the List
        }
    }
}
...