Updated 14 May 2018
In this blog, we will learn about using Custom JSON Deserializer for initiating your Java Model Class from some JSON Object (or Http response or File or any Source).
This blog will help you parse a JSON Object to your relevant Java model class,
or in better words a list of Objects of your Java Model class.
Well, I was recently working on some Android project and am using Retrofit with Gson for easily parsing and allocating the objects received from APIs.
All was going very well, until recently, I faced an issue with the parsing of ArrayList of some submodels in my Model class. The exception log was something like
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
W: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 3298 path $.bannerImages W: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:224) W: at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:37) W: at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:25) W: at retrofit2.ServiceMethod.toResponse(ServiceMethod.java:118) W: at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:212) W: at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:106) W: at okhttp3.RealCall$AsyncCall.execute(RealCall.java:135) W: at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32) W: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133) W: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607) W: at java.lang.Thread.run(Thread.java:761) W: Caused by: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 3298 path $.bannerImages W: at com.google.gson.stream.JsonReader.beginArray(JsonReader.java:350) W: at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:80) W: at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:61) W: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:129) W: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:220) W: ... 10 more |
Well, the reason for this was clearly mentioned, the GsonTypeConverter, added in my Retrofit class was actually expecting an Array but the API Returned an Object.
But in our test APIs were returning an array only.
When we looked further in the core execution of the API return values,
we found that if there is a single object then an instance of Object will be returned and when there are more than one objects then an array will be returned and this working cannot be modified for unknown reasons.
Ahhh!!!!!!!!
This blog aims to resolve the issue mentioned above.
And I have two solutions for you to look and use :
I tried both the options mentioned above and will share the code for both of them, but I personally liked the first option.
Let’s discuss the first solution in detail first.
Old (Problematic) Model class :
1 2 3 4 5 6 7 8 9 |
public class MyApiResponse { @SerializedName("results") @Expose private List<Result> results; public List<Result> getResults() { return results; } } |
New Modified And Updated Model Class :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public classMyApiResponse { @SerializedName("results") @Expose private JsonElement results; // This has been Changed. private List<Result> resultsList = null; // This has been added newly and cannot be initialized by gson. public JsonElement getResults() { return results; } public List<Result> getResultsList() { List<Result> resultList = new ArrayList<>(); // Initializing here just to cover the null pointer exception Gson gson = new Gson(); if (getResults() instanceof JsonObject) { resultList.add(gson.fromJson(getResults(), Result.class)); } else if (getResults() instanceof JsonArray) { Type founderListType = new TypeToken<ArrayList<Result>>() { }.getType(); resultList = gson.fromJson(getResults(), founderListType); } return resultList; // This is the actual list which i need and will work well with my code. } } |
The second approach is shared in this blog.
Stay tuned, Stay Super.
Keep Coding and Keep Sharing 🙂
Some other useful articles :
For other response types from APIs, you can check these articles :
If you have more details or questions, you can reply to the received confirmation email.
Back to Home
8 comments
Can you share your code so that I can have a look into your problem?
You can try these steps:
1) try printing the response received from the server
2) Match the key in quotes after @SerializedName to be exactly same as recieved from the server response.
3) If server response is null then you need to handle this case separatley and return null or empty arraylist as per your feasibility.
Still, if you face any difficaulty, then do share your code with me and i will look into the issue.
Currently i just checked with some random email and password combination and the api that you have written in your code fetched html as response and not json.