Consider the following json structure in which the key responseMessage dynamically changes between a String and a JSONObject
{ "applicationType":"1", "responseMessage":{ "surname":"Jhon", "forename":" taylor", "dob":"17081990", "refNo":"3394909238490F", "result":"Received" } }
{ "applicationType":"4", "responseMessage":"Success" }
Let us now see how to implement a custom gson converter to dynamically parse this response.
In the above cases we may create a model class like this
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | public class ResponseWrapper { @SerializedName("applicationType") @Expose private String applicationType; @SerializedName("responseMessage") @Expose private ResponseMessage responseMessage; public String getApplicationType() { return applicationType; } public void setApplicationType(String applicationType) { this.applicationType = applicationType; } public ResponseMessage getResponseMessage() { return responseMessage; } public void setResponseMessage(ResponseMessage responseMessage) { this.responseMessage = responseMessage; } } |
But this will work only when ResponseMessage is a JSONObject(the first json) but what happens if the response dynamically switches between a JSONObject and String??
One solution is that we could use gson-converter
Dependencies/Libraries to be used :
- compile 'com.squareup.retrofit2:retrofit:2.3.0'
- compile 'com.squareup.retrofit2:converter-gson:2.3.0'
First of all you will need three model classes like the following
ResponseWrapper
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | public class ResponseWrapper { @SerializedName("applicationType") @Expose private String applicationType; @SerializedName("responseMessage") @Expose private Object responseMessage; public String getApplicationType() { return applicationType; } public void setApplicationType(String applicationType) { this.applicationType = applicationType; } public Object getResponseMessage() { return responseMessage; } public void setResponseMessage(Object responseMessage) { this.responseMessage = responseMessage; } } |
ResponseMessage
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | public class ResponseMessage extends ResponseWrapper { @SerializedName("surname") @Expose private String surname; @SerializedName("forename") @Expose private String forename; @SerializedName("dob") @Expose private String dob; @SerializedName("refNo") @Expose private String refNo; @SerializedName("result") @Expose private String result; public String getSurname() { return surname; } public void setSurname(String surname) { this.surname = surname; } public String getForename() { return forename; } public void setForename(String forename) { this.forename = forename; } public String getDob() { return dob; } public void setDob(String dob) { this.dob = dob; } public String getRefNo() { return refNo; } public void setRefNo(String refNo) { this.refNo = refNo; } public String getResult() { return result; } public void setResult(String result) { this.result = result; } } |
ResponseString
1 | public class ResponseString extends ResponseWrapper { } |
Here we have subclassed ResponseMessage, ResponseString from ResponseWrapper. The reason for this approach is that deserialize() method defined in UserResponseDeserializer have a return type of ResponseWrapper, so either of the subclassed classes can be returned from deserialize() depending on the response is an object/string
So implement a custom gson converter as shown below
UserResponseDeserializer(custom deserialiser)
1 2 3 4 5 6 7 8 9 10 11 12 13 | public class UserResponseDeserializer implements JsonDeserializer<ResponseWrapper> { @Override public ResponseWrapper deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { if (((JsonObject) json).get("responseMessage") instanceof JsonObject){ return new Gson().fromJson(json, ResponseMessage.class); } else { return new Gson().fromJson(json, ResponseString.class); } } } |
The deserialize() method is an overridden method that provides us with the json to be parsed. Here we can check the type of the json(json object/string) using instanceof method and decide whether to return ResponseMessage.class/ResponseString.class
Now pass the above UserResponseDeserializer to our GsonConverterFactory to complete the implementation of our custom gson converter as shown below
Retrofit 2.0 Implementation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | Gson userDeserializer = new GsonBuilder().setLenient().registerTypeAdapter(ResponseWrapper.class, new UserResponseDeserializer()).create(); Retrofit retrofit = new Retrofit.Builder() .baseUrl("base_url") .addConverterFactory(GsonConverterFactory.create(userDeserializer)) .build(); UserService request = retrofit.create(UserService.class); Call<ResponseWrapper> call1=request.listAllUsers(); call1.enqueue(new Callback<ResponseWrapper>() { @Override public void onResponse(Call<ResponseWrapper> call, Response<ResponseWrapper> response) { ResponseWrapper responseWrapper=response.body(); Log.i("DYNAMIC RESPONSE", String.valueOf(response.body().getResponseMessage())); } @Override public void onFailure(Call<ResponseWrapper> call, Throwable t) { } }); |
Thats it! you can now parse a json key which dynamically switches as a json object/string. Similar procedures can be followed for json arrays as well(just check
instanceof JsonArray)You may also check this example which provides free source code download