I'm using Retrofit
to perform REST requests to our server. One of these requests return an array
of objects that, once deserialized in POJO
s, extend from the abstract class Event
. Event has the method getEventType()
that returns a String
, this string is the value for the key "EventType"
that I will always have inside the JSONObject
s in the array.
This is how the JSON
will look like (we have 7 type of objects as of now):
[
{
"EventType":"typeA",
"Data":"data"
},
{
"EventType":"typeB",
"OtherData":3
}
]
I'm trying to use Retrofit
and GSON
API
s to deserialize this JSON
inside the async call, to use a Callback<List<Event>>
as a parameter for the call, but I still can't find a way to do it.
You can write custom Gson TypeAdapterFactory
for this case. The thing is to determine type of an event and then to use default TypeAdapter
for that type.
That's exactly what I've done:
public class EventTypeAdapterFactory implements TypeAdapterFactory {
private static final String TAG = EventTypeAdapterFactory.class.getSimpleName();
private Map<EventType, TypeAdapter<? extends Event>> ADAPTERS = new ArrayMap<>();
private TypeAdapter<Event> baseTypeAdapter;
private TypeAdapter<JsonElement> elementAdapter;
private TypeAdapter<EventType> eventTypeAdapter;
@Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
if (!Event.class.isAssignableFrom(type.getRawType())) return null;
ADAPTERS.put(EventType.TYPE_A, gson.getDelegateAdapter(this, TypeToken.get(TypeAEvent.class)));
ADAPTERS.put(EventType.TYPE_B, gson.getDelegateAdapter(this, TypeToken.get(TypeBEvent.class)));
baseTypeAdapter = gson.getDelegateAdapter(this, TypeToken.get(Event.class));
elementAdapter = gson.getAdapter(JsonElement.class);
eventTypeAdapter = gson.getAdapter(EventType.class);
return (TypeAdapter<T>) new EventTypeAdapter().nullSafe();
}
private class EventTypeAdapter extends TypeAdapter<Event> {
@Override public void write(JsonWriter out, Event value) throws IOException {
EventType eventType = value.getType();
TypeAdapter<? extends Event> adapter = eventType == null ? baseTypeAdapter : ADAPTERS.get(eventType);
if (value instanceof TypeAEvent) {
writeWrap(adapter, out, (TypeAEvent) value, TypeAEvent.class);
} else if (value instanceof TypeBEvent) {
writeWrap(adapter, out, (TypeBEvent) value, TypeBEvent.class);
} else {
writeWrap(adapter, out, value, Event.class);
}
}
private <T extends Event> void writeWrap(TypeAdapter<? extends Event> adapter,
JsonWriter out, T value, Class<T> dummyForT) throws IOException {
((TypeAdapter<T>)adapter).write(out, value);
}
@Override public Event read(JsonReader in) throws IOException {
JsonObject objectJson = elementAdapter.read(in).getAsJsonObject();
JsonElement typeJson = objectJson.get("EventType");
EventType eventType = eventTypeAdapter.fromJsonTree(typeJson);
if (eventType == null) {
Log.w(TAG, "Unsupported EventType: " + typeJson);
}
TypeAdapter<? extends Event> adapter = eventType == null ? baseTypeAdapter : ADAPTERS.get(eventType);
return adapter.fromJsonTree(objectJson);
}
}
}
// EventType enum, change to reflect your values.
enum EventType {
TYPE_A, TYPE_B;
}
// Base Event type and its successors.
class Event {
@SerializedName("EventType")
private EventType type;
public EventType getType() {
return type;
}
}
class TypeAEvent extends Event {
@SerializedName("Data")
public String data;
}
class TypeBEvent extends Event {
@SerializedName("OtherData")
public int otherData;
}
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