I've JSON structure like follows -
{
"status": true,
"message": "Registration Complete.",
"data": {
"user": {
"username": "user88",
"email": "[email protected]",
"created_on": "1426171225",
"last_login": null,
"active": "1",
"first_name": "User",
"last_name": "",
"company": null,
"phone": null,
"sign_up_mode": "GOOGLE_PLUS"
}
}
}
Above format is common . Only data
key can hold different types of information like user
, product
, invoice
etc.
I want to keep status
, message
and data
keys same in every rest response. data
will be treated according to status
and message
will be displayed to user.
So basically, above format is desired in all apis. Only information inside data
key will be different each time.
And I've setup a following class and set it up as gson converter - MyResponse.java
public class MyResponse<T> implements Serializable{
private boolean status ;
private String message ;
private T data;
public boolean isStatus() {
return status;
}
public void setStatus(boolean status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
Deserializer.java
class Deserializer<T> implements JsonDeserializer<T>{
@Override
public T deserialize(JsonElement je, Type type, JsonDeserializationContext jdc) throws JsonParseException{
JsonElement content = je.getAsJsonObject();
// Deserialize it. You use a new instance of Gson to avoid infinite recursion to this deserializer
return new Gson().fromJson(content, type);
}
}
And used it as follows -
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
gsonBuilder.registerTypeAdapter(MyResponse.class, new Deserializer<MyResponse>());
...... ..... ....
restBuilder.setConverter(new GsonConverter(gsonBuilder.create()));
Service interface is as follows -
@POST("/register")
public void test1(@Body MeUser meUser, Callback<MyResponse<MeUser>> apiResponseCallback);
@POST("/other")
public void test2(Callback<MyResponse<Product>> apiResponseCallback);
Problem
I can access status
and message
fields from inside callback. But information inside data
key is not parsed and model like MeUser
and Product
always returns as empty.
If I change json structure to following above code works perfectly -
{
"status": true,
"message": "Registration Complete.",
"data": {
"username": "user88",
"email": "[email protected]",
"created_on": "1426171225",
"last_login": null,
"active": "1",
"first_name": "User",
"last_name": "",
"company": null,
"phone": null,
"sign_up_mode": "GOOGLE_PLUS"
}
}
How can I have it worked with specifying separate key inside data
object and parse it successfully ?
If I can suggest to change something in json is that you have to add at one new field that defines the type of data, so json should look like below:
{
"status": true,
"message": "Registration Complete.",
"dataType" : "user",
"data": {
"username": "user88",
"email": "[email protected]",
"created_on": "1426171225",
"last_login": null,
"active": "1",
"first_name": "User",
"last_name": "",
"company": null,
"phone": null,
"sign_up_mode": "GOOGLE_PLUS"
}
}
The MyResponse
class has to have new filed DataType
so it should look like below:
public class MyResponse<T> implements Serializable{
private boolean status ;
private String message ;
private DataType dataType ;
private T data;
public DataType getDataType() {
return dataType;
}
//... other getters and setters
}
The DataType
is an enum which defines type of data. You have to pass Data.class as param in constructor. For all data types you have to create new classes. DataType
enum should look like below:
public enum DataType {
@SerializedName("user")
USER(MeUser.class),
@SerializedName("product")
Product(Product.class),
//other types in the same way, the important think is that
//the SerializedName value should be the same as dataType value from json
;
Type type;
DataType(Type type) {
this.type = type;
}
public Type getType(){
return type;
}
}
The desarializator for Json should looks like below:
public class DeserializerJson implements JsonDeserializer<MyResponse> {
@Override
public MyResponse deserialize(JsonElement je, Type type, JsonDeserializationContext jdc)
throws JsonParseException {
JsonObject content = je.getAsJsonObject();
MyResponse message = new Gson().fromJson(je, type);
JsonElement data = content.get("data");
message.setData(new Gson().fromJson(data, message.getDataType().getType()));
return message;
}
}
And when you create RestAdapter
, in the line where you register Deserializator, you should use this :
.registerTypeAdapter(MyResponse.class, new DeserializerJson())
Other classes (types of data) you define like standard POJO for Gson in separated classes.
Your issue is because the data
attribute is defined as T
which you expect to be of types MeUser
, Product
, etc, but is actually of an object which has inner attribute like user
. To resolve this, you need to introduce another level of classes which has the required attributes user
, product
, invoice
etc. This can be easily achieved using static inner classes.
public class MeUser{
private User user;
public static class User{
private String username;
//add other attributes of the User class
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With