Hi. For some time now, we've been using Retrofit with Moshi, and we used a Converter to unwrap the usual 'envelope', like this:
@JsonClass(generateAdapter = true)
data class Envelope<T>(val data: T)
...
object EnvelopeConverter : Factory() {
override fun responseBodyConverter(
type: Type,
annotations: Array<Annotation>,
retrofit: Retrofit
): Converter<ResponseBody, *>? {
val envelopedType = Types.newParameterizedType(Envelope::class.java, type)
val delegate: Converter<ResponseBody, Envelope<Any>>? =
retrofit.nextResponseBodyConverter(this, envelopedType, annotations)
return Unwrapper(delegate)
}
private class Unwrapper<T>(
private val delegate: Converter<ResponseBody, Envelope<T>>?
) : Converter<ResponseBody, T> {
override fun convert(value: ResponseBody): T? {
return delegate?.convert(value)?.data
}
}
}
I imagine you're familiar with this - a fairly common pattern to improve the usability of responses, and in fact a long time ago Jake shared a Gson version of the same thing in a presentation.
We have explored replicating this under Kotlin Serialization and we cannot determine a way forward. The direct equivalent seems to be something like:
@Keep
@Serializable
data class Envelope<T>(val data: T)
...
object EnvelopeConverter : Factory() {
@OptIn(ExperimentalStdlibApi::class)
override fun responseBodyConverter(
type: Type,
annotations: Array<Annotation>,
retrofit: Retrofit
): Converter<ResponseBody, *>? {
val envelopeContentsType = KTypeProjection.invariant(type::class.starProjectedType)
val envelopedType = Envelope::class.createType(listOf(envelopeContentsType)).javaType
val delegate: Converter<ResponseBody, Envelope<Any>>? =
retrofit.nextResponseBodyConverter(this, envelopedType, annotations)
return Unwrapper(delegate)
}
private class Unwrapper<T>(
private val delegate: Converter<ResponseBody, Envelope<T>>?
) : Converter<ResponseBody, T> {
override fun convert(value: ResponseBody): T? {
return delegate?.convert(value)?.data
}
}
}
This fails because Envelope<Any> is (reasonably) not understood by Kotlin Serialization:
kotlinx.serialization.SerializationException: Serializer for class 'Any' is not found.
I might be missing something but I can't see that we can be more specific with the class when only supplied with type as a method parameter.
I should add that in our case, it is important that we configure Retrofit centrally and make it available to decentralised modules to parse their own data objects. We therefore cannot centrally define all the possible polymorphic types and have the resultant Kotlin Serialization polymorphic parser make sense of the type for us.
I think therefore this would fall to Retrofit to support. Have you got any opinions or advice please?
Hi. For some time now, we've been using Retrofit with Moshi, and we used a
Converter
to unwrap the usual 'envelope', like this:I imagine you're familiar with this - a fairly common pattern to improve the usability of responses, and in fact a long time ago Jake shared a Gson version of the same thing in a presentation.
We have explored replicating this under Kotlin Serialization and we cannot determine a way forward. The direct equivalent seems to be something like:
This fails because
Envelope<Any>
is (reasonably) not understood by Kotlin Serialization:I might be missing something but I can't see that we can be more specific with the class when only supplied with
type
as a method parameter.I should add that in our case, it is important that we configure Retrofit centrally and make it available to decentralised modules to parse their own data objects. We therefore cannot centrally define all the possible polymorphic types and have the resultant Kotlin Serialization polymorphic parser make sense of the type for us.
I think therefore this would fall to Retrofit to support. Have you got any opinions or advice please?