JakeWharton/retrofit2-kotlinx-serialization-converter

Crash

HAHH9527 opened this issue · 0 comments

I read readme Response body types (e.g., Call<User>) and @Body types (e.g., @Body user: User) will now use the supplied serializer. can use Call.
But I use retrofit2.Call and Call.await() get response crash.
And I use retrofit2.Response no crash.

Log

     Caused by: kotlinx.serialization.json.internal.JsonDecodingException: Polymorphic serializer was not found for missing class discriminator ('null')
    JSON input: {"code":0,"message":null,"data":"888888"}
        at kotlinx.serialization.json.internal.JsonExceptionsKt.JsonDecodingException(JsonExceptions.kt:24)
        at kotlinx.serialization.json.internal.JsonExceptionsKt.JsonDecodingException(JsonExceptions.kt:32)
        at kotlinx.serialization.json.internal.PolymorphicKt.throwSerializerNotFound(Polymorphic.kt:76)
        at kotlinx.serialization.json.internal.PolymorphicKt.decodeSerializableValuePolymorphic(Polymorphic.kt:66)
        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.internal.connection.RealCall$AsyncCall.run(RealCall.kt:519)
        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:920)
--> GET http://****/login?mobile=13900000000
--> END GET
<-- 200 OK http://****/login?mobile=13900000000 (58ms)
Date: Fri, 06 May 2022 07:53:30 GMT
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Origin
{"code":0,"message":null,"data":"888888"}
<-- END HTTP (41-byte body)

Code

import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import retrofit2.Retrofit

object RetrofitClient {
    private fun defaultOkHttpClient(): OkHttpClient {
        return OkHttpClient.Builder()
            .addCommonParamInterceptor()
            .addHttpLoggingInterceptor()
            .build()
    }

    fun getRetrofit(
        baseUrl: String = ServiceEnv.getEnv().baseUrl,
        okHttpClient: OkHttpClient = defaultOkHttpClient()
    ): Retrofit {
        return Retrofit.Builder()
            .baseUrl(baseUrl)
            .client(okHttpClient)
            .addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
            .build()
    }
    
    val json = Json {
        ignoreUnknownKeys = true
        coerceInputValues = true
    }
}
import kotlinx.serialization.Serializable

@Serializable
data class BaseResponse<T>(
    val code: Int,
    val message: String?,
    val data: T,
)
import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.Query

interface UserService {
    @GET("${ServiceEnv.usrbiz}/sms/login")
    suspend fun loginGetSmsCode(@Query("mobile") mobile: String): Call<BaseResponse<String>>
}
object TestManager {
    private val loginService = RetrofitClient.getRetrofit().create<UserService>()
    private val successCode = intArrayOf(0, 10019, 201004)

    suspend fun getSmsCode(mobile: String) = requestNetworkData {
        loginService.loginGetSmsCode(mobile)
    }

    suspend inline fun <T> requestNetworkData(apiCall: () -> Call<BaseResponse<T>>): T {
        val call = apiCall()
        return call.processGetData()
    }

    suspend fun <T> Call<BaseResponse<T>>.processGetData(): T {
        val response = await()
        if (successCode.contains(response.code)) {
            return response.data
        } else {
            throw RuntimeException("request failed")
        }
    }
}

How can success

import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.Query

interface UserService {
    @GET("${ServiceEnv.usrbiz}/sms/login")
    suspend fun loginGetSmsCodeResponse(@Query("mobile") mobile: String): Response<BaseResponse<String>>
}
import retrofit2.Response
import retrofit2.create

object TestManager {
    private val loginService = RetrofitClient.getRetrofit().create<UserService>()
    private val successCode = intArrayOf(0, 10019, 201004)

    suspend fun getSmsCodeResponse(mobile: String) = requestNetworkDataResponse {
        loginService.loginGetSmsCodeResponse(mobile)
    }

    suspend inline fun <T> requestNetworkDataResponse(getResponse: () -> Response<BaseResponse<T>>): T {
        val body = getResponse().body()
        return if (body != null && successCode.contains(body.code)) body.data
        else throw RuntimeException("request failed")
    }
}