PaleNisko
PaleNisko

Reputation: 11

Retrofit 2.1.0 + Gson. Serialize @Body which is subclass

I have a problem with Retrofit, which uses static type of class, not runtime type to serialize it's RequestBody.

Let's say i have two classes :

class A{
   String a;
}

class B extends A{
   String b;
}

Now i want to make a request:

@Post("/someUrl)
Observable<Void> someRequest(@Body A body);

I want to pass A or B instances to someRequest, and serialize them properly basing on class type.

I tried to use RuntimeTypeAdapterFactory, it worked well but it gave me additional field after serialization (type field). My A, B classes will be only used for serialization.

Upvotes: 1

Views: 845

Answers (2)

Sibdever
Sibdever

Reputation: 11

I know, this question is old, but really normal solution is to register custom type adapter when you create your Retrofit instance. Example here.

Also it works with Kotlin sealed classes:

sealed class AbstractBody(val bodyType: String) {

    data class A(
        val someDataA: String
    ) : AbstractBody(bodyType = "A")

    data class B(
        val someDataB: String
    ) : AbstractBody(bodyType = "B")
}

And in your retrofit creator:

private fun createRetrofit(baseUrl: String, okHttpClient: OkHttpClient) =
    Retrofit.Builder()
        .baseUrl(baseUrl)
        .client(okHttpClient)
        .addConverterFactory(
            GsonConverterFactory.create(GsonBuilder().withCustomAdapters().create())
        )
        .build()

private fun GsonBuilder.withCustomAdapters() = apply {
    registerTypeAdapter(
        AbstractBody::class.java,
        JsonSerializer<AbstractBody> { src, _, context ->
            when (src) {
                is AbstractBody.A ->
                    context.serialize(src, AbstractBody.A::class.java)
                is AbstractBody.B ->
                    context.serialize(src, AbstractBody.B::class.java)
            }
        }
    )
}

Upvotes: 1

tynn
tynn

Reputation: 39863

Consider just overloading the method itself:

@Post("/someUrl)
Observable<Void> someRequest(@Body A body);
@Post("/someUrl)
Observable<Void> someRequest(@Body B body);

Upvotes: 1

Related Questions