Here is my Retrofit
instance:
@Provides
@Singleton
ApiManager provideApiManager() {
RxJava2CallAdapterFactory rxAdapter = RxJava2CallAdapterFactory.create();
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addNetworkInterceptor(new StethoInterceptor())
.build();
Gson gson = new GsonBuilder().create();
GsonConverterFactory converterFactory = GsonConverterFactory.create(gson);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(AppConstants.BASE_URL)
.addConverterFactory(converterFactory)
.addCallAdapterFactory(rxAdapter)
.client(okHttpClient)
.build();
return retrofit.create(ApiManager.class);
}
Model:
class AbstractMessage {
String id;
}
class TextMessage extends AbstractMessage {
String textMessage;
}
class ImageMessage extends AbstractMessage {
String url;
String text;
}
Request:
@GET("direct/messages")
Observable<List<AbstractMessage>> getMessages(@Header("Authorization") String authHeader, @Body RequestObject request);
Executing request:
apiManager.getMessages(authHeader, requestObject)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<List<AbstractMessage>>() {
@Override
public void accept(List<AbstractMessage> messages) throws Exception {
...
}
});
When I execute a request I receive a collection of AbstractMessage
objects. The JSON
can contain both text and image messages. In my case JSON
converter creates AbstractMessage
and maps only the id
field. How can I make converter to create TextMessage
and ImageMessage
objects map all matching fields and then cast it to AbstractMessage
. Or there may be some other solution.
You must create a RuntimeTypeAdapterFactory for the objects AbstractMessage, TextMessage and ImageMessage and then you must set it into the gson instance.
Suppose that you have those objects:
public class Animal {
protected String name;
protected String type;
public Animal(String name, String type) {
this.name = name;
this.type = type;
}
}
public class Dog extends Animal {
private boolean playsCatch;
public Dog(String name, boolean playsCatch) {
super(name, "dog");
this.playsCatch = playsCatch;
}
}
public class Cat extends Animal {
private boolean chasesLaser;
public Cat(String name, boolean chasesLaser) {
super(name, "cat");
this.chasesLaser = chasesLaser;
}
}
This below is the RuntimeTypeAdapter that you need in order to deserialize (and serialize) correctly those objects:
RuntimeTypeAdapterFactory<Animal> runtimeTypeAdapterFactory = RuntimeTypeAdapterFactory
.of(Animal.class, "type")
.registerSubtype(Dog.class, "dog")
.registerSubtype(Cat.class, "cat");
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(runtimeTypeAdapterFactory)
.create();
The class RuntimeTypeAdapterFactory.java is not shipped with the Gson package, so you have to download it manually.
You can read more about the runtime adapter here and here
Please note that the title of your question should be "Polymorphism with Gson"
I hope it helps.
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