Kotlin / kotlinx.serialization

Kotlin multiplatform / multi-format serialization
Apache License 2.0
5.33k stars 618 forks source link

Deserialization Polymorpism with Generic #1313

Open Mitti30 opened 3 years ago

Mitti30 commented 3 years ago

I have to work with an API that responds with JSON:

so i have built the corresponding classes.

@Serializable
abstract class SimpleResponse<out T>

@Serializable
data class Error<out T>(
    val message: String,
    val code: Int
) : SimpleResponse<T>()

@Serializable
data class Success<out T>(
    val data: T
) : SimpleResponse<T>()

the generic in Success can be something like this:

@Serializable
data class InitResponse(
    val location:Location,
     val rights:Rights,
)

i defined the SerializationModule like this:

module= SerializersModule {
        polymorphic(Any::class){
            subclass(InitResponse::class)
        }

        polymorphic(SimpleResponse::class) {
            subclass(Success.serializer(PolymorphicSerializer(InitResponse::class)))
        }
    }

But its throwing me

kotlinx.serialization.json.internal.JsonDecodingException: Polymorphic serializer was not found for missing class discriminator ('null')

Did i forgot to define something?

sandwwraith commented 3 years ago

Can you please add the Json you're trying to deserialize? It looks like it does not have 'type' field

Mitti30 commented 3 years ago

It's not having the type field, thats why I give him the type with the function call.

@POST("/")
suspend fun initDevice(@Body body:SimpleRequest):SimpleResponse<InitResponse>
Mitti30 commented 3 years ago

Ok, now i added


class ResultSerializer<T>(private val dataSerializer: KSerializer<T>): KSerializer<Success<T>>{
    override val descriptor: SerialDescriptor=dataSerializer.descriptor
    override fun deserialize(decoder: Decoder): Success<T> = Success(dataSerializer.deserialize(decoder))

    override fun serialize(encoder: Encoder, value: Success<T>) =dataSerializer.serialize(encoder,value.data)

}

but im stuck with the same bug as shown in this issue ^^

ln-12 commented 3 years ago

Hey @sandwwraith, I'm working with @Mitti30 on this problem in our app. As he described, we have two cases:

We now want to define a deserializer that can be used by retrofit to either return the Success<T> or Error object. As the json comes directly from our server, there is no type field provided. So we can not infer the type by looking at the json. Instead, it is only known from the type of the retrofit call as @Mitti30 showed above. I hope it is a bit easier to understand now.

sandwwraith commented 3 years ago

I see the problem. The good approach for it is to remove polymorphic serialization and write your own custom serializer, like in this comment: https://github.com/Kotlin/kotlinx.serialization/issues/1313#issuecomment-770994374 . However, due to the mentioned kapt issue, it's impossible for now. Probably, a viable workaround can be to move models and serializers to the separate Gradle module, which is not processed by kapt.