Describe the bug
given the same json as input, when trying to deserialize to the following class structure, I get a kotlinx.serialization.MissingFieldException crash when using decodeFromJsonElement() as seen in the code example.
Here are the relevant data structures:
@Serializable
abstract class Resource {
abstract val id: String
}
@Serializable
@SerialName("typeClassWithCyclicRelationship")
data class ClassWithCyclicRelationship(
override val id: String,
val field: Int,
val parentRelationship: ClassWithCyclicRelationship?,
) : Resource()
class JsonAPIDocumentTest {
private lateinit var json: Json
@BeforeTest
fun setup() {
json = jsonFormat {
polymorphic(Resource::class) {
subclass(ClassWithCyclicRelationship::class)
}
}
}
@Test
fun `decodingFromJsonElement crashes if there is a null parent reference`() {
val transformedJSONDataObject = JsonObject(mapOf(
"type" to JsonPrimitive("typeClassWithCyclicRelationship"),
"id" to JsonPrimitive("1"),
"field" to JsonPrimitive("123"),
"parentRelationship" to JsonObject(mapOf(
"type" to JsonPrimitive("typeClassWithCyclicRelationship"),
"id" to JsonPrimitive("2"),
"field" to JsonPrimitive("456"),
"parentRelationship" to JsonNull
)),
))
val output = json.decodeFromJsonElement(Resource.serializer(), transformedJSONDataObject)
assertEquals(
ClassWithCyclicRelationship(
id = "1",
field = 123,
parentRelationship = ClassWithCyclicRelationship(
id = "2",
field = 456,
parentRelationship = null,
),
),
output,
)
}
@Test
fun `decodingFromJsonString works just fine given the exact same structure!`() {
val transformedJSONDataObject = JsonObject(mapOf(
"type" to JsonPrimitive("typeClassWithCyclicRelationship"),
"id" to JsonPrimitive("1"),
"field" to JsonPrimitive("123"),
"parentRelationship" to JsonObject(mapOf(
"type" to JsonPrimitive("typeClassWithCyclicRelationship"),
"id" to JsonPrimitive("2"),
"field" to JsonPrimitive("456"),
"parentRelationship" to JsonNull
)),
))
val output = json.decodeFromString(Resource.serializer(),transformedJSONDataObject.toString())
assertEquals(
ClassWithCyclicRelationship(
id = "1",
field = 123,
parentRelationship = ClassWithCyclicRelationship(
id = "2",
field = 456,
parentRelationship = null,
),
),
output,
)
}
}
Full stacktrace
Fields [id, field, parentRelationship] are required for type with serial name 'typeClassWithCyclicRelationship', but they were missing
kotlinx.serialization.MissingFieldException: Fields [id, field, parentRelationship] are required for type with serial name 'typeClassWithCyclicRelationship', but they were missing
at app//kotlinx.serialization.internal.PluginExceptionsKt.throwMissingFieldException(PluginExceptions.kt:20)
at app//com.peakon.network.jsonapi.ClassWithCyclicRelationship.<init>(TestResources.kt:135)
at app//com.peakon.network.jsonapi.ClassWithCyclicRelationship$$serializer.deserialize(TestResources.kt:135)
at app//com.peakon.network.jsonapi.ClassWithCyclicRelationship$$serializer.deserialize(TestResources.kt:135)
at app//kotlinx.serialization.json.internal.PolymorphicKt.decodeSerializableValuePolymorphic(Polymorphic.kt:61)
at app//kotlinx.serialization.json.internal.AbstractJsonTreeDecoder.decodeSerializableValue(TreeJsonDecoder.kt:52)
at app//kotlinx.serialization.internal.TaggedDecoder.decodeSerializableValue(Tagged.kt:207)
at app//kotlinx.serialization.internal.TaggedDecoder$decodeNullableSerializableElement$1.invoke(Tagged.kt:289)
at app//kotlinx.serialization.internal.TaggedDecoder.tagBlock(Tagged.kt:297)
at app//kotlinx.serialization.internal.TaggedDecoder.decodeNullableSerializableElement(Tagged.kt:288)
at app//com.peakon.network.jsonapi.ClassWithCyclicRelationship$$serializer.deserialize(TestResources.kt:135)
at app//com.peakon.network.jsonapi.ClassWithCyclicRelationship$$serializer.deserialize(TestResources.kt:135)
at app//kotlinx.serialization.json.internal.PolymorphicKt.decodeSerializableValuePolymorphic(Polymorphic.kt:61)
at app//kotlinx.serialization.json.internal.AbstractJsonTreeDecoder.decodeSerializableValue(TreeJsonDecoder.kt:52)
at app//kotlinx.serialization.json.internal.TreeJsonDecoderKt.readPolymorphicJson(TreeJsonDecoder.kt:33)
at app//kotlinx.serialization.json.internal.PolymorphicKt.decodeSerializableValuePolymorphic(Polymorphic.kt:71)
at app//kotlinx.serialization.json.internal.AbstractJsonTreeDecoder.decodeSerializableValue(TreeJsonDecoder.kt:52)
at app//kotlinx.serialization.json.internal.TreeJsonDecoderKt.readJson(TreeJsonDecoder.kt:25)
at app//kotlinx.serialization.json.Json.decodeFromJsonElement(Json.kt:117)
at app//com.peakon.network.jsonapi.JsonAPIDocumentTest.decodingFromJsonElement crashes if there is a null parent reference(JsonAPIDocumentTest.kt:201)
at java.base@17.0.6/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base@17.0.6/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at java.base@17.0.6/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.base@17.0.6/java.lang.reflect.Method.invoke(Unknown Source)
at app//org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at app//org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at app//org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at app//org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at app//org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at app//org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at app//org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at app//org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at app//org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at app//org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at app//org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at app//org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at app//org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at app//org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at app//org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at app//org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at app//org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:108)
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:40)
at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:60)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:52)
at java.base@17.0.6/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base@17.0.6/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at java.base@17.0.6/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.base@17.0.6/java.lang.reflect.Method.invoke(Unknown Source)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
at jdk.proxy2/jdk.proxy2.$Proxy5.processTestClass(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker$2.run(TestWorker.java:176)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:113)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:65)
at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
Expected behavior
That decodeFromJsonElement() behaves the same as decodeFromString().
Never mind, seems like the bug is fixed in version 1.6.0! The above test cases are working as expected without the crash after updating to the latest library version
Describe the bug given the same json as input, when trying to deserialize to the following class structure, I get a
kotlinx.serialization.MissingFieldException
crash when usingdecodeFromJsonElement()
as seen in the code example.Here are the relevant data structures:
global method
test class that highlights the two cases
Full stacktrace
Expected behavior That
decodeFromJsonElement()
behaves the same asdecodeFromString()
.Environment