Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
263 views
Welcome To Ask or Share your Answers For Others

1 Answer

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...