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.
You are basically trying to deserialize a response to one of two types -
Success
orError
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.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.