Search code examples
androidretrofit2jsonschema2pojo

Retrofit 2.0 parse dynamic json from same POJO class


What I Have

I have a server success response

{
  "response_code": 200,
  "status": "success",
  "message": "enqiry chat fetched successfully",
  "meta_data": {
    "count": "6"
  },
  "data": {
    "enquiries": [
    ]
  }
}

When Error , the same API returns

{
  "response_code": 500,
  "status": "error",
  "meta_data": {
    "count": 0
  },
  "data": [],
  "message": "Please specify all required parameter to add enquiries"
}

What happened

At error scenario the data is changed from JsonObject to JsonArray

My Problem

At success response everything works fine (because I made POJO class from success response )

At error response my app crashes saying Tried to read object but found array

What I can't do

I can't change the back-end , because it is already developed and works for website also .

What I did

I googled and found many solutions which I cannot relate to my issues

POJO

public class ReviewModel {

    @SerializedName("data")
    private Data mData;

    public Data getData() {
        return mData;
    }

    public void setData(Data data) {
        mData = data;
    }

    public class Data {

        @SerializedName("reviews")
        private List<Review> mReviews;

        public List<Review> getReviews() {
            return mReviews;
        }

        public void setReviews(List<Review> reviews) {
            mReviews = reviews;
        }

    }



    public class Review {

        @SerializedName("comment_date")
        private String mCommentDate;

        public String getCommentDate() {
            return mCommentDate;
        }

        public void setCommentDate(String comment_date) {
            mCommentDate = comment_date;
        }

        }
}

Solution

  • My solution is to use some thing like this ,

    import com.google.gson.annotations.Expose;
    import com.google.gson.annotations.SerializedName;
    
    import java.util.List;
    
    public class Response {
    
        @SerializedName("response_code")
        @Expose
        private Integer responseCode;
        @SerializedName("status")
        @Expose
        private String status;
        @SerializedName("meta_data")
        @Expose
        private MetaData metaData;
        @SerializedName("data")
        @Expose
        private List<Object> data = null;
        @SerializedName("message")
        @Expose
        private String message;
    
        public Integer getResponseCode() {
            return responseCode;
        }
    
        public void setResponseCode(Integer responseCode) {
            this.responseCode = responseCode;
        }
    
        public String getStatus() {
            return status;
        }
    
        public void setStatus(String status) {
            this.status = status;
        }
    
        public MetaData getMetaData() {
            return metaData;
        }
    
        public void setMetaData(MetaData metaData) {
            this.metaData = metaData;
        }
    
        public List<Object> getData() {
            return data;
        }
    
        public void setData(List<Object> data) {
            this.data = data;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    
    
        public class MetaData {
    
            @SerializedName("count")
            @Expose
            private Integer count;
    
            public Integer getCount() {
                return count;
            }
    
            public void setCount(Integer count) {
                this.count = count;
            }
    
        }
     }
    

    And use a Custom Array Adapter with Gson,

    ArrayAdapter class

    import com.google.gson.Gson;
    import com.google.gson.TypeAdapter;
    import com.google.gson.TypeAdapterFactory;
    import com.google.gson.reflect.TypeToken;
    
    import java.lang.reflect.ParameterizedType;
    import java.util.ArrayList;
    import java.util.List;
    
    public class ArrayAdapterFactory implements TypeAdapterFactory {
    
        @Override
        @SuppressWarnings({"unchecked", "rawtypes"})
        public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> type) {
    
            TypeAdapter<T> typeAdapter = null;
            try {
                if (type.getRawType() == List.class || type.getRawType() == ArrayList.class) {
    
                    typeAdapter = new ArrayAdapter(gson,
                            (Class) ((ParameterizedType) type.getType())
                                    .getActualTypeArguments()[0]);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            return typeAdapter;
    
        }
    
    }
    

    ArrayAdapterFactory class

    import com.google.gson.Gson;
    import com.google.gson.TypeAdapter;
    import com.google.gson.stream.JsonReader;
    import com.google.gson.stream.JsonToken;
    import com.google.gson.stream.JsonWriter;
    
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    
    
    class ArrayAdapter<T> extends TypeAdapter<List<T>> {
    
        private Class<T> adapterclass;
        private Gson gson;
    
        public ArrayAdapter(Gson gson, Class<T> adapterclass) {
            this.adapterclass = adapterclass;
            this.gson = gson;
        }
    
        @Override
        public List<T> read(JsonReader reader) throws IOException {
    
            List<T> list = new ArrayList<T>();
    
            final JsonToken token = reader.peek();
            System.out.println(token);
            // Handling of Scenario 2( Check JavaDoc for the class) :
            if (token == JsonToken.STRING || token == JsonToken.NUMBER ||
                    token == JsonToken.BOOLEAN) {
                T inning = (T) gson.fromJson(reader, adapterclass);
                list.add(inning);
            } else if (token == JsonToken.BEGIN_OBJECT) {
                // Handling of Scenario 1(Check JavaDoc for the class) :
                T inning = (T) gson.fromJson(reader, adapterclass);
                list.add(inning);
            } else if (token == JsonToken.BEGIN_ARRAY) {
                reader.beginArray();
                while (reader.hasNext()) {
                    @SuppressWarnings("unchecked")
                    T inning = (T) gson.fromJson(reader, adapterclass);
                    list.add(inning);
                }
                reader.endArray();
            }
    
            return list;
        }
    
        @Override
        public void write(JsonWriter writer, List<T> value) throws IOException {
    
        }
    }
    

    And register the adapter factory like this,

    Gson gson  = new GsonBuilder().registerTypeAdapterFactory(new ArrayAdapterFactory()).create();
    

    This will help to deserialise the json string.