The code looks like this:
Class definitions:
sealed class MetaItem<M : Meta> {
@Serializable
class ValueItem<M : Meta>(val value: Value) : MetaItem<M>()
@Serializable
class SingleNodeItem<M : Meta>(val node: M) : MetaItem<M>()
@Serializable
class MultiNodeItem<M : Meta>(val nodes: List<M>) : MetaItem<M>()
}
interface Meta {
val items: Map<String, MetaItem<out Meta>>
}
abstract class MetaNode<M : MetaNode<M>> : Meta {
abstract override val items: Map<String, MetaItem<M>>
}
@Serializable
class SealedMeta internal constructor(override val items: Map<String, MetaItem<SealedMeta>>) : MetaNode<SealedMeta>()
The save method throws exception inside serialization framework:
java.lang.NoSuchFieldException: INSTANCE
at java.base/java.lang.Class.getField(Class.java:1958)
at kotlinx.serialization.SerializationKt.invokeSerializerGetter(Serialization.kt:54)
at kotlinx.serialization.SerializationKt.serializer(Serialization.kt:22)
at kotlinx.serialization.internal.SerialCache.lookupSerializer$kotlinx_serialization_runtime(Polymorphic.kt:80)
at kotlinx.serialization.ResolvingKt.serializerByClass(Resolving.kt:56)
at kotlinx.serialization.ResolvingKt.serializerByValue(Resolving.kt:51)
at kotlinx.serialization.PolymorphicSerializer.save(PolymorphicSerializer.kt:27)
The framework obviously tries to find external serializator for ValueItem. Id does not seem that it should.
The kotlin plugin version is 1.2.70 and serialization plugin version is 0.6.2. Serializable objects and tests are declared in common module and tests are run via kotlin-test junit4 wrapper.
I run into the exactly same problem. Except i got this issue in general with generics.
@Serializable`
class GenericDto<T>(@SerialId(1) val value: T)
@Serializable
class OtherDto(@SerialId(1) val value: String)
is your GenericDto is an inheritor of class you're trying to serialize? I think that this is a problem with polymorphic serialization: PolymorphicSerializer determines concrete class at runtime, and tries to obtain its default serializer, but generic classes do not have static one (therefore NoSuchField: INSTANCE exception).
It would be probably hard to fix, because of non-reified generics at runtime. Probably new design of polymorphic serialization will help us to fix this.
No there is no inheritance. I just tried to serialize the GenericDto with an reference of the OtherDto to / from Protobuf. Inheritance would be anyway a bit tricky i think. At least for Protobuf, since there is no support for that in the original project.
I really curious about the upcoming design of this very nice project.
Then it's rather strange because original stacktrace has PolymorphicSerializer in it. Can you post your stacktrace, please?
We will try to abstract inheritance over different formats (mainly by just writing concrete class and its name) See https://github.com/Kotlin/KEEP/blob/serialization/proposals/extensions/serialization.md#polymorphism for details.
java.lang.NoSuchFieldException: INSTANCE
at java.lang.Class.getField(Class.java:1703)
at kotlinx.serialization.SerializationKt.invokeSerializerGetter(Serialization.kt:54)
at kotlinx.serialization.SerializationKt.serializer(Serialization.kt:22)
at kotlinx.serialization.ScopeKt.klassSerializer(Scope.kt:44)
at ch.sourcemotion.scape.mqtt.api.dto.TestDtoTest.protobufTest(TestDtoTest.kt:25)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:106)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:38)
at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:66)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:117)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:155)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:137)
at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:404)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
at java.lang.Thread.run(Thread.java:748)
@Serializable
class GenericDto<T>(@SerialId(1) val value: T)
@Serializable
class OtherDto(@SerialId(1) val value: String)
class TestDtoTest {
@Test
fun protobufTest() {
val dump = ProtoBuf.dump(GenericDto(OtherDto("")))
val load = ProtoBuf.load<GenericDto<OtherDto>>(dump)
}
}
Hi, we've got the same problem and solved by manually registering all serializers for each sealed class.
Problem that PolymorphicSerializer cannot resolve nested classes as these classes is not loaded in java class loader (or kotlin cannot find them).
example:
object TelemetryServiceActionSerializer {
init {
registerSerializer(TelemetryService.Action.Ping::class.qualifiedName!!, TelemetryService.Action.Ping.serializer())
registerSerializer(TelemetryService.Action.CurrentLoggedDrivers::class.qualifiedName!!, TelemetryService.Action.CurrentLoggedDrivers.serializer())
registerSerializer(TelemetryService.Action.DeviceInformation::class.qualifiedName!!, TelemetryService.Action.DeviceInformation.serializer())
registerSerializer(TelemetryService.Action.LoggedIn::class.qualifiedName!!, TelemetryService.Action.LoggedIn.serializer())
registerSerializer(TelemetryService.Action.LoggedOut::class.qualifiedName!!, TelemetryService.Action.LoggedOut.serializer())
}
Also crashes on multiplatform project (JVM target):
Exception in thread "main" java.lang.NoSuchFieldException: INSTANCE
at java.lang.Class.getField(Class.java:1703)
at kotlinx.serialization.SerializationKt.invokeSerializerGetter(Serialization.kt:56)
at kotlinx.serialization.SerializationKt.compiledSerializer(Serialization.kt:23)
at kotlinx.serialization.PlatformUtilsKt.serializer(PlatformUtils.kt:27)
at io.ktor.client.features.json.serializer.KotlinxSerializer.lookupSerializerByType(KotlinxSerializer.kt:105)
at io.ktor.client.features.json.serializer.KotlinxSerializer.read(KotlinxSerializer.kt:85)
at io.ktor.client.features.json.JsonFeature$Feature$install$2.invokeSuspend(JsonFeature.kt:107)
at io.ktor.client.features.json.JsonFeature$Feature$install$2.invoke(JsonFeature.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:268)
at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:67)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:141)
at io.ktor.util.pipeline.SuspendFunctionGun.proceedWith(PipelineContext.kt:151)
at io.ktor.client.features.HttpCallValidator$Companion$install$2.invokeSuspend(HttpCallValidator.kt:97)
at io.ktor.client.features.HttpCallValidator$Companion$install$2.invoke(HttpCallValidator.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:268)
at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:67)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:141)
at io.ktor.util.pipeline.SuspendFunctionGun.execute(PipelineContext.kt:161)
at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:27)
at io.ktor.client.call.HttpClientCall.receive(HttpClientCall.kt:84)
at moe.yuuta.bot.Main.main(Main.kt:200)
Particularly this problem is solved by https://github.com/Kotlin/kotlinx.serialization/commit/673ef19214b6bad4d5e211964a068e04caa15583. However, this is only an improvement so correct error message could be shown: Can't locate argument-less serializer for $this. For generic classes, such as lists, please provide serializer explicitly.. It means that serializers for any unusual classes (polymorphic, generic, etc) can't be obtained via regular Java reflection.
If you have polymorphic class, make sure it uses correct polymorphic serializer and all its subclasses are registered. If you have a generic class, make sure that you've provided serializer explicitly. For Ktor adapters, it is currently possible to use only Java type token. KType support for user-defined generic classes is on the way.
Getting the same error in JVM using Ktor with JsonFeature using KotlinxSerialization(). The classes are organized this way:
@Serializable
data class Container2<T>(val bar: T)
@Serializable
data class Container1<T>(val foo: List<Container2<T>>)
Where T is a simple data class without any method or interface.
@lamba92 While you tell us that T is a simple data class this is not something that the compiler would know seeing this code. If you look at the actual code that triggers the exception you will see that this is based upon implicit reflection (not advised). The method (https://github.com/Kotlin/kotlinx.serialization/blob/341e7d86fd2fc2bb7320c76075631596658587ab/runtime/jvmMain/src/kotlinx/serialization/Serialization.kt#L41-L45) actually tries its best to find a serializer for whatever data object is needing to be serialized. It can't find it (but it assumes incorrectly that getField returns a null rather than crashing). As such you get an impossible error message rather than a friendly one.
Eventually after understanding better this library the error was (of course) mine. I was not using the deserializers build by the plugin! Now it works like a charm :)
The error message is very very unfriendly tho!
Most helpful comment
Also crashes on multiplatform project (JVM target):