Reputation: 951
My response from the server is a list of different types of JSON objects, which depends on each responses' "type". I have different beans for each response type. Can Retrofit use appropriate Bean class for each response (JSON Object)in the list(JSON Array)?
{
"cards": [2]
0: {
"type": "A"
"ID": 24
"author_name": "ari"
"title": "BB"
"permalink": ""
"created_date": "2015-12-18 17:17:00"
"post_date": "Dec 18 2015"
"thumbnail": ""
"summary": "Stars"
"thumbSmall": "100.jpg"
"androidXL": "500.jpg"
}-
1: {
"type": "B"
"_id": "3290"
"read_count": 0
"categories": [1]
0: "abc"
"title": "New"
"isSticky": false
"section": [0]
"author": "zzz"
"india": false
"update_time": 1450415789694
"summary": "Social"
"scoreId": "nz"
"isActive": true
"image": ""
"timestamp": 1450385165210
"events": [1]
0: "nz"
"teams": [0]
"slug": "new"
"isDeleted": false
"score_str": "SL"
}
}
As a workaround, I have created a new Bean class, with all possible fields, almost half the fields are null for each JSON Object.
Is there a better way of doing this?
Upvotes: 0
Views: 1545
Reputation: 1113
You can create a super class which contains data common for all types and extend it:
public abstract class SuperItem {
public enum Type {
@SerializedName("A")
TYPEA(1),
@SerializedName("B")
TYPEB(2);
Type(final int value) {
this.value = value;
}
public static Type fromInteger(int i) {
switch (i) {
case 1:
return TYPEA;
case 2:
return TYPEB;
default:
Log.d("SuperItem", "No existing type for integer: " + i);
return null;
}
}
}
}
@SerializedName("type") private Type type;
}
public class AItem extends SuperItem {
}
public class BItem extends SuperItem {
}
Retrofit by default relies on GSON for JSON deserialization. You will need to create custom Gson deserializer for your implementation and set it to your RestAdapter:
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(SuperItem.class, new SuperItemDeserializer());
Gson gson = builder.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
Your custom deserializer can look like this:
public class SuperItemDeserializer implements JsonDeserializer<SuperItem> {
@Override
public SuperItem deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
Gson gson = new Gson();
SuperItem item = null;
JsonObject rootObject = json.getAsJsonObject();
int typeInt = rootObject.get("type").getAsInt();
SuperItem.Type type = SuperItem.Type.fromInteger(typeInt);
switch (type) {
case TYPEA:
item = gson.fromJson(json, AItem.class);
break;
case TYPEB:
item = gson.fromJson(json, BItem.class);
break;
default:
Log.d("TAG", "Invalid type: " + typeInt);
}
return item;
}
}
Hope the naming in the example is not too bad and you'll get the idea :)
Upvotes: 2