Treat unmatched JSON type during element deserialization of decoding … (#820)
* Treat unmatched JSON type during element deserialization of decoding exception instead of ISE to maintain decoding invariant
Fixes #816
diff --git a/runtime/commonMain/src/kotlinx/serialization/json/internal/Polymorphic.kt b/runtime/commonMain/src/kotlinx/serialization/json/internal/Polymorphic.kt
index 639b3b9..635db46 100644
--- a/runtime/commonMain/src/kotlinx/serialization/json/internal/Polymorphic.kt
+++ b/runtime/commonMain/src/kotlinx/serialization/json/internal/Polymorphic.kt
@@ -52,7 +52,7 @@
return deserializer.deserialize(this)
}
- val jsonTree = cast<JsonObject>(decodeJson())
+ val jsonTree = cast<JsonObject>(decodeJson(), deserializer.descriptor)
val discriminator = json.configuration.classDiscriminator
val type = jsonTree.getValue(discriminator).content
val actualSerializer = deserializer.findPolymorphicSerializer(this, type).cast<T>()
diff --git a/runtime/commonMain/src/kotlinx/serialization/json/internal/TreeJsonInput.kt b/runtime/commonMain/src/kotlinx/serialization/json/internal/TreeJsonInput.kt
index 6572eb0..eef6cf7 100644
--- a/runtime/commonMain/src/kotlinx/serialization/json/internal/TreeJsonInput.kt
+++ b/runtime/commonMain/src/kotlinx/serialization/json/internal/TreeJsonInput.kt
@@ -60,13 +60,13 @@
override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
val currentObject = currentObject()
return when (descriptor.kind) {
- StructureKind.LIST, is PolymorphicKind -> JsonTreeListInput(json, cast(currentObject))
+ StructureKind.LIST, is PolymorphicKind -> JsonTreeListInput(json, cast(currentObject, descriptor))
StructureKind.MAP -> json.selectMapMode(
descriptor,
- { JsonTreeMapInput(json, cast(currentObject)) },
- { JsonTreeListInput(json, cast(currentObject)) }
+ { JsonTreeMapInput(json, cast(currentObject, descriptor)) },
+ { JsonTreeListInput(json, cast(currentObject, descriptor)) }
)
- else -> JsonTreeInput(json, cast(currentObject))
+ else -> JsonTreeInput(json, cast(currentObject, descriptor))
}
}
diff --git a/runtime/commonMain/src/kotlinx/serialization/json/internal/TreeJsonOutput.kt b/runtime/commonMain/src/kotlinx/serialization/json/internal/TreeJsonOutput.kt
index ef6fc66..c7163b2 100644
--- a/runtime/commonMain/src/kotlinx/serialization/json/internal/TreeJsonOutput.kt
+++ b/runtime/commonMain/src/kotlinx/serialization/json/internal/TreeJsonOutput.kt
@@ -183,7 +183,9 @@
override fun getCurrent(): JsonElement = JsonArray(array)
}
-internal inline fun <reified T : JsonElement> cast(value: JsonElement): T {
- check(value is T) { "Expected ${T::class} but found ${value::class}" }
+internal inline fun <reified T : JsonElement> cast(value: JsonElement, descriptor: SerialDescriptor): T {
+ if (value !is T) {
+ throw JsonDecodingException(-1, "Expected ${T::class} as the serialized body of ${descriptor.serialName}, but had ${value::class}")
+ }
return value
}
diff --git a/runtime/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPolymorphismExceptionTest.kt b/runtime/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPolymorphismExceptionTest.kt
new file mode 100644
index 0000000..578f39d
--- /dev/null
+++ b/runtime/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPolymorphismExceptionTest.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.polymorphic
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+import kotlin.test.*
+
+class JsonPolymorphismExceptionTest : JsonTestBase() {
+
+ @Serializable
+ abstract class Base
+
+ @Serializable
+ @SerialName("derived")
+ class Derived(val nested: Nested = Nested()) : Base()
+
+ @Serializable
+ class Nested
+
+ @Test
+ fun testDecodingException() = parametrizedTest { useStreaming ->
+ val serialModule = SerializersModule {
+ polymorphic(Base::class) {
+ subclass<Derived>()
+ }
+ }
+
+ assertFailsWith<JsonDecodingException> {
+ Json(context = serialModule).parse(Base.serializer(), """{"type":"derived","nested":null}""", useStreaming)
+ }
+ }
+}
\ No newline at end of file