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
Hello, what in case if we have multiple model class with such dynamic jsons, do we need to add all of them to ResponseWrapper class ?
ReplyDelete