I am using sealed class to handle api response. I am trying some code but getting some error of serialization. I tried this solution as well but it not working. Can you guys help me out on this problem. I am new in serialization field.

import kotlinx.serialization.Serializable

sealed class ApiResponse<out T : Any> {

    data class Success<out T : Any>(
        val data: T?
    ) : ApiResponse<T>()

    data class Error(
        val exception: Throwable? = null,
        val responseCode: Int = -1
    ) : ApiResponse<Nothing>()

    fun handleResult(onSuccess: ((responseData: T?) -> Unit)?,onError: ((error: Error) -> Unit)?) {
        when (this) {
            is Success -> {
                onSuccess?.invoke(this.data)
            }
            is Error -> {
                onError?.invoke(this)
            }
        }
    }
}

@Serializable
data class ErrorResponse(
    val errorCode: Int,
    val errorMessage: String
)

Api

import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.request.*
import kotlinx.coroutines.flow.Flow
import kotlinx.serialization.Serializable

class KtorCountryApi(private val httpClient: HttpClient) {
    suspend fun getCart(): Flow<ApiResponse<KtorCountriesResponse>> {
        println("api call")
        return httpClient.get {
            url("https://shop-api.example-stg2.com/api/v1/address/country")
        }.body()
    }
}

@Serializable
data class KtorCountriesResponse(
    val items: List<KtorCountry>? = null
)

@Serializable
data class KtorCountry(
    val id: String? = null,
    val isCurrentCountry: Boolean? = null,
    var isoAlpha2Code: String? = null,
    var name: String? = null,
    var phonePrefix: String? = null,
    val usesPerAreaShipping: Boolean? = null
)

Client

actual fun httpClient(config: HttpClientConfig<*>.() -> Unit) = HttpClient(OkHttp) {
    config(this)
    install(Logging) {
        logger = Logger.SIMPLE
        level = LogLevel.BODY
    }
    install(ContentNegotiation) {
        json(Json {
            prettyPrint = true
            ignoreUnknownKeys = true
            explicitNulls = false
        })
    }
    engine {
        config {
            retryOnConnectionFailure(true)
            connectTimeout(30, TimeUnit.SECONDS)
            readTimeout(40, TimeUnit.SECONDS)
        }
    }
    defaultRequest {
        header("Client-Version", Platform().versionCode)
    }
    install(Auth) {
        bearer {
            loadTokens {
                BearerTokens(tokenProvider.accessToken, "")
            }
            refreshTokens {
                val response =
                    client.post("https://example-stg2.com/api/v1/session/refresh") {
                        markAsRefreshTokenRequest()
                        contentType(ContentType.Application.Json)
                        setBody(KtorSessionCommand(tokenProvider.refreshToken))
                    }
                if (response.status == HttpStatusCode.Unauthorized) {
                    null
                } else {
                    val ktorLoginResponse = response.body<KtorLoginResponse>()
                    ktorLoginResponse.accessToken?.let { ktorAccessToken ->
                        ktorAccessToken.accessToken?.let { accessToken ->
                            ktorAccessToken.refreshToken?.let { refreshToken ->
                                BearerTokens(accessToken, refreshToken)
                            }
                        }
                    }
                }
            }
        }
    }
}

Error

2022-04-22 10:50:28.614 8365-8365/com.example.app.dev E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.app.dev, PID: 8365
    kotlinx.serialization.json.internal.JsonDecodingException: Polymorphic serializer was not found for missing class discriminator ('null')
    JSON input: .....pping":false,"phonePrefix":"263","isCurrentCountry":false}]}
        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 io.ktor.serialization.kotlinx.KotlinxSerializationConverter.deserialize(KotlinxSerializationConverter.kt:55)
        at io.ktor.client.plugins.contentnegotiation.ContentNegotiation$Plugin$install$2.invokeSuspend(ContentNegotiation.kt:135)
        at io.ktor.client.plugins.contentnegotiation.ContentNegotiation$Plugin$install$2.invoke(Unknown Source:13)
        at io.ktor.client.plugins.contentnegotiation.ContentNegotiation$Plugin$install$2.invoke(Unknown Source:6)
        at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:123)
        at io.ktor.util.pipeline.SuspendFunctionGun.proceed(SuspendFunctionGun.kt:81)
        at io.ktor.client.HttpClient$4.invokeSuspend(HttpClient.kt:170)
        at io.ktor.client.HttpClient$4.invoke(Unknown Source:11)
        at io.ktor.client.HttpClient$4.invoke(Unknown Source:6)
        at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:123)
        at io.ktor.util.pipeline.SuspendFunctionGun.proceed(SuspendFunctionGun.kt:81)
        at io.ktor.client.plugins.logging.Logging$setupResponseLogging$2.invokeSuspend(Logging.kt:167)
        at io.ktor.client.plugins.logging.Logging$setupResponseLogging$2.invoke(Unknown Source:11)
        at io.ktor.client.plugins.logging.Logging$setupResponseLogging$2.invoke(Unknown Source:6)
        at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:123)
        at io.ktor.util.pipeline.SuspendFunctionGun.proceed(SuspendFunctionGun.kt:81)
        at io.ktor.util.pipeline.SuspendFunctionGun.proceedWith(SuspendFunctionGun.kt:91)
        at io.ktor.client.plugins.HttpCallValidator$Companion$install$2.invokeSuspend(HttpCallValidator.kt:140)
        at io.ktor.client.plugins.HttpCallValidator$Companion$install$2.invoke(Unknown Source:13)
        at io.ktor.client.plugins.HttpCallValidator$Companion$install$2.invoke(Unknown Source:6)
        at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:123)
        at io.ktor.util.pipeline.SuspendFunctionGun.proceed(SuspendFunctionGun.kt:81)
        at io.ktor.util.pipeline.SuspendFunctionGun.execute$ktor_utils(SuspendFunctionGun.kt:101)
        at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:77)
        at io.ktor.client.call.HttpClientCall.body(HttpClientCall.kt:87)
        at com.example.kotlinmultiplatformsharedmodule.KtorCountryApi.getCart(KtorCountryApi.kt:53)
        at com.example.kotlinmultiplatformsharedmodule.KtorCountryApi$getCart$1.invokeSuspend(Unknown Source:14)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
        at android.os.Handler.handleCallback(Handler.java:938)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loopOnce(Looper.java:201)
        at android.os.Looper.loop(Looper.java:288)
        at android.app.ActivityThread.main(ActivityThread.java:7839)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)

I am not understand what this error mean. Can someone explain me why do we need extra code for this error to solve.

1

There are 1 answers

0
Sheinbergon On
  1. You are basically trying to deserialize a response to one of two types - Success or Error but give provide your code no explicit means to discriminate between the two classes in regards to which type to use based on input fields.

  2. For a correct solution, please follow the information found here and use either @SerialName on each of your polymorphic sub-types in order to help the server-side properly decode them.