27

Hi I have a problem with serialization of class in kotlin

build.gradl.kt

...
plugins {
    application
    kotlin("jvm") version "1.6.21"
    kotlin("plugin.serialization").version("1.6.21")
}
...
depenedancies{
...
    implementation("io.ktor:ktor-server-content-negotiation:$ktor_version")
    implementation("io.ktor:ktor-serialization-kotlinx-json:$ktor_version")
} 

Response.kt

import kotlinx.serialization.*
...
interface BaseResponse<T>

@Serializable
data class PaginatedResponse<T>(
    val prev: Int?,
    val next: Int?,
    val totalCount: Int = 0,
    val totalPages: Int = 0,
    val data: T? = null,
    val message: String? = null
) : BaseResponse<T>

usage

...
  return PaginatedResponse<List<User>>(
                prev,
                next,
                totalCount,
                totalPages,
                users
            )

data which I am passing looks quite healthy enter image description here and the error which is thrown when return is reached is next

kotlinx.serialization.SerializationException: Serializer for class 'PaginatedResponse' is not found.
Mark the class as @Serializable or provide the serializer explicitly.

doing call.respond(User(...)) will not throw any error

so if I remove generic and make the PaginatedResponse non generic it will work but is not reusable anymore

@Serializable
data class PaginatedResponse(
    val prev: Int?,
    val next: Int?,
    val totalCount: Long = 0,
    val totalPages: Long = 0,
    val data: List<User>? = null,
    val message: String? = null
) : BaseResponse<User>
Edric
  • 24,639
  • 13
  • 81
  • 91
Victor Orlyk
  • 1,454
  • 2
  • 15
  • 27

3 Answers3

52

after reading some documentation I found that the error in my case was mainly due to two misconfigurations that I had:

first: I needed to add the plugin in the gradle at the app and project level, I solved this as follows: adding in gradle.project the next line: id 'org.jetbrains.kotlin.plugin.serialization' version '1.6.21'

As is seen in the following image: gradle.module

and after that, adding in the gradle.app the next line in the plugins block : id 'kotlinx-serialization'

As is seen in the following image: gradle.app

secondly, the problem was that when the classes were serialized at runtime, the proguard generated conflicts, for this I had to add a few lines to the file proguard-rules.pro project tree

And add the following lines into the file:

### your config ....

# Keep `Companion` object fields of serializable classes.
# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.
-if @kotlinx.serialization.Serializable class **
-keepclassmembers class <1> {
   static <1>$Companion Companion;
}

# Keep `serializer()` on companion objects (both default and named) of serializable classes.
-if @kotlinx.serialization.Serializable class ** {
   static **$* *;
}
-keepclassmembers class <2>$<3> {
   kotlinx.serialization.KSerializer serializer(...);
}

# Keep `INSTANCE.serializer()` of serializable objects.
-if @kotlinx.serialization.Serializable class ** {
   public static ** INSTANCE;
}
-keepclassmembers class <1> {
   public static <1> INSTANCE;
   kotlinx.serialization.KSerializer serializer(...);
}

# @Serializable and @Polymorphic are used at runtime for polymorphic serialization.
-keepattributes RuntimeVisibleAnnotations,AnnotationDefault

As is seen in the following image:  file proguard-rules.pro

I finally synced the gradle, compiled again and everything worked fine.

Reference documentation: https://github.com/Kotlin/kotlinx.serialization#gradle-with-plugins-block

Jere Schneider
  • 766
  • 7
  • 8
  • 1
    This helped hugely. Adding `id 'kotlinx-serialization'` to the app module's build.gradle file fixed for me. – James Woodall Apr 06 '23 at 10:27
  • 2
    Note: ProGuard rules are now [included as part of the library](https://github.com/Kotlin/kotlinx.serialization/blob/master/rules/common.pro) (first included in [1.5.0-RC](https://github.com/Kotlin/kotlinx.serialization/releases/tag/v1.5.0-RC)), as mentioned in the [readme](https://github.com/Kotlin/kotlinx.serialization#android). (Note that named companion objects are not included and still require additional configuration as mentioned) – Edric Apr 13 '23 at 18:25
  • This answer applies maybe for an Android project, but for Ktor project it's not really relevant. The original question is about Ktor project – Stoycho Andreev May 03 '23 at 07:33
2

I once had a similar issue. I assume that you're using Ktor and the function returning the PaginatedReponse somehow gets passed as a parameter to call.respond(), right?

ApplicationCall.respond() is a reified inline function. In my case the variable I passed to call.respond() had lost it's static type information through a when-expression with multiple return types. The compiler inferred a common return type of the when expression of "Any".

In my case, this resulted in the same error message as you got there. Confusingly it said there was no serializer found for the specific type although the reified type was Any.

Perhaps you have the same issue as I did. Hope that I was able to help.

Definition of respond

Intex32
  • 41
  • 1
  • 4
  • yeah, it is ktor app, and i am passing PaginatedResponse to call.respond(). And the thing that before the call of call.respond all the data is present there. will add screen shot to the question. – Victor Orlyk Apr 24 '22 at 17:39
  • It also happened to me in my Kotlin JS application. This extension with retified is unbelivable dangerous, not a single warning in the docs or from the lint. – Allan Veloso Apr 30 '22 at 02:14
  • This problem or misconeption is rather related to the language feature of "reified types" and the nature of Kotlin's type inference. It applies for any other functions with reified type the same. It would be best to put it into the Kotlin docs https://kotlinlang.org/docs/inline-functions.html#reified-type-parameters – Intex32 May 01 '22 at 08:13
  • How did you resolve your issue? – Bink Apr 14 '23 at 17:23
  • Well I had to move the call of the respond function into the individual branches of the when statement, as this is where they still have their specific type information. So I had one "respond" per branch instead of one "respond" outside the branch. – Intex32 Apr 16 '23 at 14:44
0

Like the above answer, he may be missing the type

I solved this by adding <reified T> on the sub method.