retrofit2-kotlinx-serialization-converter
retrofit2-kotlinx-serialization-converter copied to clipboard
Response with null field which is not included in data class field caused polymorphic error
Using this example response https://gist.githubusercontent.com/ronjunevaldoz/934758f12a3b0a63be1bde73da20c3df/raw/210ad1695695a2abdf4c14057bff422ba970c1ee/response-jobs.json
Sample data class
@Serializable
data class ActualJob(
val id: Int,
val id_uuid: String,
val title: String,
val description: String?,
val reference: String = "",
@SerialName("order_number")
val orderNumber: String?,
val type : String,
val billed_client : String?,
val address: JobAddress?,
val job_status : JobStatus?,
val priority: Int = 1,
val clients : List<Client>,
val entity_permissions : EntityPermission
)
Using Retrofit2 service (Coroutine Flow) result as ResponseBody
@GET("api/v1/job/actual")
suspend fun getJobs(): ResponseBody // working
// Assume all data classes are serialized already
val jsonString= repository.getJob().string() or from response
val jobs = AppJson.decodeFromString<ActualJob>(jsonString)
// This will works fine
Using Retrofit2 service (Coroutine Flow) result as Flow list of ActualJob
@GET("api/v1/job/actual")
suspend fun getJobs(): Flow<List<ActualJob>> // With error
// sample implementation
repository.getJobs()
.catch { e-> emit(e)}
.collectLatest { jobs-> emit(jobs) }
Sample working converter setup
val AppJson = Json {
ignoreUnknownKeys = true
encodeDefaults = true // to include default value in encoding
coerceInputValues = true
classDiscriminator = "#class" // to avoid type as default reserve discriminator
}
builder .client(loggingClient) .addConverterFactory(AppJson.asConverterFactory(ContentType)) .build()
Dependencies (Kotlin Version: 1.5.31)
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0'
Error encountered
kotlinx.serialization.json.internal.JsonDecodingException: Expected class kotlinx.serialization.json.JsonObject (Kotlin reflection is not available) as the serialized body of kotlinx.serialization.Polymorphic<Flow>, but had class kotlinx.serialization.json.JsonArray (Kotlin reflection is not available)
at kotlinx.serialization.json.internal.JsonExceptionsKt.JsonDecodingException(JsonExceptions.kt:24)
at kotlinx.serialization.json.internal.PolymorphicKt.decodeSerializableValuePolymorphic(Polymorphic.kt:91)
at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeSerializableValue(StreamingJsonDecoder.kt:36)
at kotlinx.serialization.json.Json.decodeFromString(Json.kt:100)
at com.jakewharton.retrofit2.converter.kotlinx.serialization.Serializer$FromString.fromResponseBody(Serializer.kt:30)
at com.jakewharton.retrofit2.converter.kotlinx.serialization.DeserializationStrategyConverter.convert(DeserializationStrategyConverter.kt:11)
at com.jakewharton.retrofit2.converter.kotlinx.serialization.DeserializationStrategyConverter.convert(DeserializationStrategyConverter.kt:7)
at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:243)
at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:153)
at okhttp3.RealCall$AsyncCall.run(RealCall.kt:138)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:919)
Debug investigation
I believe decoding from string with loader causes the issue, i tried evaluating it with decodeFromString(string)
it is working find, but decodeFromString(loader, string)
the error was thrown.
@OptIn(ExperimentalSerializationApi::class) // Experimental is only for subtypes.
class FromString(override val format: StringFormat) : Serializer() {
override fun <T> fromResponseBody(loader: DeserializationStrategy<T>, body: ResponseBody): T {
val string = body.string()
return format.decodeFromString(loader, string)
}
override fun <T> toRequestBody(contentType: MediaType, saver: SerializationStrategy<T>, value: T): RequestBody {
val string = format.encodeToString(saver, value)
return RequestBody.create(contentType, string)
}
}
Possible solution
I am thinking to use this sample answer https://stackoverflow.com/a/68466759/2801777
Not sure what this is but if I had to guess it's some behavior of kotlinx.serialization or your chosen serialization format and not specifically this library. Please provide a failing test case or reproducing sample that I can use to diagnose the problem.
Not sure what this is but if I had to guess it's some behavior of kotlinx.serialization or your chosen serialization format and not specifically this library. Please provide a failing test case or reproducing sample that I can use to diagnose the problem.
Hi, I believe this is a serializer issue, but when I try manual decoding and encoding the data class, from a response string. It is working fine. I have provided the investigation details above.
@JakeWharton
When I use "minifyEnabled true" that error message is thrown...
kotlinx.serialization.json.internal.JsonDecodingException: Polymorphic serializer was not found for missing class discriminator ('null') JSON input: {"success":true,"message":"Otp sent successfully","data":{"countryCode":"880","mobileNo":" 99999999","type":"New","testOtp":63401}}