简体   繁体   中英

Moshi and Retrofit2 : Expected BEGIN_OBJECT but was STRING

I'm facing this issue on my project. I receive from api call a response like:

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

And relative data classes are

data class ResponseClass(
    val aResponse : AResponse
) 

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

Not it happen that when "listOfSomething" is empty, i receive this response:

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

that throws (of course) the exception

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

How can i solve it?

You are getting this error as when there is data you get array and when no data get string which is wrong in retrofit.

If there are no data insise listOfSomething then ask backend to send empty array instead of string.

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

instead of

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

If your json result is gonna change depends of the result, first of all your backend is doing a bad job, then you have to "hack" a little bit your app to adapt the code...

Your POJO class should be :

data class MyResponse(
    val aResponse: AResponse
)

data class AResponse(
    val listOfSomething: Any
)

You can declare it as Any which is not a good practise, but it's a workaround to make it work according to your backend. Is like in Java adding Object

Then you can do something in your 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
        }
    }
}

First, your backend implementation is wrong. You should not send an empty string to represent an empty array.

If you can't fix it on backend side because the API are not under your control, you can try with something like this:

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;
    }
}

like suggested here: https://github.com/square/moshi/issues/295#issuecomment-299636385

And then annotate your listOfSomething with: IgnoreJsonArrayError annotation

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM