1

I have followed a tutorial to create a custom serialization for my generic class. But I am still stacked with error:

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

My ApiResponse class looks like this:

@Serializable(with = ApiResponseSerializer::class)
class ApiResponse<T>(
    @SerialName("data")
    val data: T? = null,
    @SerialName("error")
    val error: ApiError? = null
)

And ApiResponseSerializer, that is used in annotation above is:

class ApiResponseSerializer<T>(private val dataSerializer: KSerializer<T>) : KSerializer<ApiResponse<T>> {
    override val descriptor: SerialDescriptor = buildClassSerialDescriptor("ApiResponseDataSerializer") {
        val dataDescriptor = dataSerializer.descriptor
        element("data", dataDescriptor)
        element("error", ApiError.serializer().descriptor)
    }
    override fun deserialize(decoder: Decoder): ApiResponse<T> =
        decoder.decodeStructure(descriptor) {
            var data: T? = null
            var error: ApiError? = null
            loop@ while (true) {
                when (val i = decodeElementIndex(descriptor)) {
                    0 -> data = decodeSerializableElement(descriptor, i, dataSerializer)
                    1 -> error = decodeSerializableElement(descriptor, i, ApiError.serializer())
                    CompositeDecoder.DECODE_DONE -> break
                    else -> throw SerializationException("Unknown index $i")
                }
            }
            ApiResponse(data, error)
        }
    override fun serialize(encoder: Encoder, value: ApiResponse<T>) {
        encoder.encodeStructure(descriptor) {
            encodeNullableSerializableElement(descriptor, 0, dataSerializer, value.data)
            encodeNullableSerializableElement(descriptor, 1, ApiError.serializer(), value.error)
        }
    }
}

Then, when I’m trying to serialize my data object, I receive an exception I’ve mentioned above.

Json.encodeToString(ApiResponse(data = response.data))
---- OR ----
Json.encodeToString(ApiResponse.serializer(T::class.serializer()), ApiResponse(data = response.data))

Can someone tell me where I’m wrong? Thank you.

P. Savrov
  • 1,064
  • 4
  • 17
  • 29
  • 1
    Works fine on my machine for `Json.encodeToString(ApiResponse(data = "some string"))`. What is the type of `response.data`? Is it marked with `@Serializable` annotation? – Михаил Нафталь Oct 24 '20 at 12:42
  • 1
    What is the point of `@loop` label? – Михаил Нафталь Oct 24 '20 at 12:42
  • In my case, data can be represented by any data class with `@Serializable` annotation. The `@loop` annotation appears in doc. to `KSerializer` class, and, how I do understand it, marks the place where the program should return in case of `break`. more: https://kotlinlang.org/docs/reference/returns.html#break-and-continue-labels P.S. now it works. Maybe it was by the bad cache or smth else. Anyway thank you for your test and support – P. Savrov Oct 25 '20 at 03:42

3 Answers3

0

The code in the question was good, the program fails due to bad cache or something else.

P. Savrov
  • 1,064
  • 4
  • 17
  • 29
0

It seems to be kapt's bug.

I found this comment from the serialization repository :

However, due to the mentioned kapt issue, it's impossible for now. Probably, a viable workaround can be to move models and serializers to the separate Gradle module, which is not processed by kapt.

And there are more issues about this problem :

SorrowBeaver
  • 88
  • 1
  • 4
0

kotlinx.serialization uses a specific plugin to generate serializers. If you start your code directly in the IDE (e.g. via the main method), the plugin is not used.

To ensure that generated serializers are available on the classpath, try calling your code from a unit test and start this test with the appropriate Gradle task.