kotlinx.serialization icon indicating copy to clipboard operation
kotlinx.serialization copied to clipboard

Can't generate a Serializer for a generic implementation with non-serializable parameter Type

Open IARI opened this issue 6 years ago • 3 comments

The following fails to compile:

import kotlinx.serialization.Serializable

class NotSerializable

@Serializable
abstract class AA<T>

@Serializable
class A : AA<NotSerializable>()

with

e: java.lang.IllegalStateException: Backend Internal error: Exception during code generation Cause: Back-end (JVM) Internal error: Serializer for element of type NotSerializable has not been found. To use context serializer as fallback, explicitly annotate element with @ContextualSerialization Element is unknown

Even though serialization of T types is actually not used here.

This fails on version >=1.3.20. but did work before (on 1.3.11). Is there any workaround for such a scenario?

IARI avatar Feb 28 '19 00:02 IARI

Annotate NotSerializable with ContextualSerialization:

@Serializable
class A : AA<@ContextualSerialization NotSerializable>()

This happens because A need to know how to serialize its parent: AA may have or have not private properties of type T, so we need to construct serializer for actual type argument T = NotSerializable.

sandwwraith avatar Mar 01 '19 11:03 sandwwraith

In ran into a similar issue. In my case, I prefer not to apply @ContextualSerialization to each and every type parameter of extending classes of AA. I also don't find this communicates the intent very clearly.

Instead, I decided to create a NotSerializable serializer:

/**
 * A dummy serializer which can be applied to types that are never expected to be serialized,
 * but for which the compiler is trying to generate/retrieve a serializer. E.g., types used as generic type parameters.
 * Applying `@Serializable( with = NotSerializable::class )` to those types ensures compilation succeeds,
 * without having to actually make them serializable.
 */
object NotSerializable : KSerializer<Any>
{
    private val exception = SerializationException(
        "Types annotated as `@Serializable( with = NotSerializable::class )` are never expected to be serialized. " +
        "The serializer is only defined since the compiler does not know this, causing a compilation error." )

    override val descriptor: SerialDescriptor = SerialClassDescImpl( "This should never be serialized." )
    override fun deserialize( decoder: Decoder ): Any = throw exception
    override fun serialize( encoder: Encoder, obj: Any ) = throw exception
}

Which can be used as follows:

@Serializable( with = NotSerializable::class )
class SomeUnserializableClass

It would be nicer if this intent could be expressed using something along the lines of a dedicated @NotSerializable annotation.

Whathecode avatar Jul 12 '19 10:07 Whathecode

Related: #1263

sandwwraith avatar Dec 18 '24 16:12 sandwwraith