-2

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?

giozh
  • 9,868
  • 30
  • 102
  • 183

3 Answers3

3

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": ""
    }
}
Swati
  • 146
  • 1
  • 10
  • 1
    Exactly, why would Backend even be returning a String as oppose to an Expected List – Tosin Onikute Feb 03 '20 at 10:17
  • 3
    Many backends are dumbster fire. I personally experienced situations where company employed students who don't know what they are doing most of the time. They are cheap alternative :D – xinaiz Feb 03 '20 at 11:39
  • my issue is somehow similar to this. the array is returned as a string and there are forward slashes as well. would have been nice if moshi already has a built in factory for this – chitgoks Dec 17 '20 at 03:36
2

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
        }
    }
}
Skizo-ozᴉʞS ツ
  • 19,464
  • 18
  • 81
  • 148
  • @giozh Have you solved this? In order to help other people if this helped you feel free to upvote and mark this as a correct answer. Thanks :) – Skizo-ozᴉʞS ツ Jun 28 '21 at 13:39
1

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

MatPag
  • 41,742
  • 14
  • 105
  • 114