kotlinx.serialization: Serializing generics is not working

Serializing the following Container<Data> class fails with:

INSTANCE
java.lang.NoSuchFieldException: INSTANCE
@Serializable
class Container<T>(val data: T)

@Serializable
class Data(val text: String)

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 15 (7 by maintainers)

Most upvoted comments

If you have non-generic class (say, MyData(val a: Container<Data>), you can use simple MyData.serializer(). However, if your class has type arguments, you have to provide serializers explicitly for each generic type.

In some observable future, we are going to invent mechanism such compiler could also insert serializers on call-site, not only in internal serialization calls. This should make things easier.

Error seems like you’re trying to serialize container by simple JSON.stringify(Container(Data(..))). Because of generic type erasure, this wouldn’t work: On runtime, framework can’t determine how to serialize data in container. Thus Container class does not have default (object) serializer, and thus there is no field INSTANCE.

Right way to do it is to create serializer explicitly: JSON.stringify(Container.serializer(Data.serializer()), Container(Data(...)))

You can learn more here

PolymorphicSerializer has no state so is threadsafe, but the generated serializers for classes have no specific thread support. That is, if you change the object while it is being serialized this will likely lead to incorrect values. Custom serializer code can of course add locks where appropriate or be externally synchronized.

Remember there are two kinds of “serializers” there is the code specific to types (which includes PolymorphicSerializer) and the output/input such as JSON. The JSON class has state and no synchronization so isn’t threadsafe, the serializers providing the information to json themselves have the limited threadsafety I described above…

JSON.parse also requires explicit serializer in your case - generic arguments are not reified, even if the function reified itself 😦 So in your case you should end with something like

val boxedDataSerial = Container.serializer(Data.serializer())
val json = JSON.stringify(boxedDataSerial, Container(Data("test")))
val parsed = JSON.parse(boxedDataSerial, json)
assertEquals("test", parsed.data.text) // ok

Yes, for now IDEA experience is slightly poor because it don’t recognize some synthetic functions. You can improve it by following this instructions: https://github.com/Kotlin/kotlinx.serialization#working-in-intellij-idea

Ok thanks, I tried it (see below) but it still fails with the same error. What am I doing wrong? I’m using version 0.3.

Btw. IDEA is not recognising Container.serializer but only Container::class.serializer. However, if I use the later I can’t do Container::class.serializer(Data::class.serializer()) Why is that?

@Serializable
class Container<T>(val data: T)

@Serializable
class Data(val text: String)

@Test
fun testGenerics() {
    val json = JSON.stringify(Container.serializer(Data.serializer()), Container(Data("test")))
    val parsed = JSON.parse<Container<Data>>(json)
    assertEquals("test", parsed.data.text)
}