summaryrefslogtreecommitdiff
path: root/formats/json-tests/commonTest/src/kotlinx/serialization/features/PolymorphismTest.kt
diff options
context:
space:
mode:
Diffstat (limited to 'formats/json-tests/commonTest/src/kotlinx/serialization/features/PolymorphismTest.kt')
-rw-r--r--formats/json-tests/commonTest/src/kotlinx/serialization/features/PolymorphismTest.kt184
1 files changed, 184 insertions, 0 deletions
diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/PolymorphismTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/PolymorphismTest.kt
new file mode 100644
index 00000000..c4938871
--- /dev/null
+++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/PolymorphismTest.kt
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+import kotlinx.serialization.test.*
+import kotlin.test.*
+
+class PolymorphismTest : JsonTestBase() {
+
+ @Serializable
+ data class Wrapper(
+ @Id(1) @Polymorphic val polyBase1: PolyBase,
+ @Id(2) @Polymorphic val polyBase2: PolyBase
+ )
+
+ private val module: SerializersModule = BaseAndDerivedModule + SerializersModule {
+ polymorphic(
+ PolyDerived::class,
+ PolyDerived.serializer()
+ )
+ }
+
+ private val json = Json { useArrayPolymorphism = true; serializersModule = module }
+
+ @Test
+ fun testInheritanceJson() = parametrizedTest { jsonTestingMode ->
+ val obj = Wrapper(
+ PolyBase(2),
+ PolyDerived("b")
+ )
+ val bytes = json.encodeToString(Wrapper.serializer(), obj, jsonTestingMode)
+ assertEquals(
+ """{"polyBase1":["kotlinx.serialization.PolyBase",{"id":2}],""" +
+ """"polyBase2":["kotlinx.serialization.PolyDerived",{"id":1,"s":"b"}]}""", bytes
+ )
+ }
+
+ @Test
+ fun testSerializeWithExplicitPolymorphicSerializer() = parametrizedTest { jsonTestingMode ->
+ val obj = PolyDerived("b")
+ val s = json.encodeToString(PolymorphicSerializer(PolyDerived::class), obj, jsonTestingMode)
+ assertEquals("""["kotlinx.serialization.PolyDerived",{"id":1,"s":"b"}]""", s)
+ }
+
+ object PolyDefaultDeserializer : JsonTransformingSerializer<PolyDefault>(PolyDefault.serializer()) {
+ override fun transformDeserialize(element: JsonElement): JsonElement = buildJsonObject {
+ put("json", JsonObject(element.jsonObject.filterKeys { it != "type" }))
+ put("id", 42)
+ }
+ }
+
+ object EvenDefaultSerializer : SerializationStrategy<PolyBase> {
+ override val descriptor = buildClassSerialDescriptor("even") {
+ element<String>("parity")
+ }
+
+ override fun serialize(encoder: Encoder, value: PolyBase) {
+ encoder.encodeStructure(descriptor) {
+ encodeStringElement(descriptor, 0, "even")
+ }
+ }
+ }
+
+ object OddDefaultSerializer : SerializationStrategy<PolyBase> {
+ override val descriptor = buildClassSerialDescriptor("odd") {
+ element<String>("parity")
+ }
+
+ override fun serialize(encoder: Encoder, value: PolyBase) {
+ encoder.encodeStructure(descriptor) {
+ encodeStringElement(descriptor, 0, "odd")
+ }
+ }
+ }
+
+ @Test
+ fun testDefaultDeserializer() = parametrizedTest { jsonTestingMode ->
+ val withDefault = module + SerializersModule {
+ polymorphicDefaultDeserializer(PolyBase::class) { name ->
+ if (name == "foo") {
+ PolyDefaultDeserializer
+ } else {
+ null
+ }
+ }
+ }
+
+ val adjustedJson = Json { serializersModule = withDefault }
+ val string = """
+ {"polyBase1":{"type":"kotlinx.serialization.PolyBase","id":239},
+ "polyBase2":{"type":"foo","key":42}}""".trimIndent()
+ val result = adjustedJson.decodeFromString(Wrapper.serializer(), string, jsonTestingMode)
+ assertEquals(Wrapper(PolyBase(239), PolyDefault(JsonObject(mapOf("key" to JsonPrimitive(42))))), result)
+
+ val replaced = string.replace("foo", "bar")
+ assertFailsWithMessage<SerializationException>("not found") { adjustedJson.decodeFromString(Wrapper.serializer(), replaced, jsonTestingMode) }
+ }
+
+ @Test
+ fun testDefaultDeserializerForMissingDiscriminator() = parametrizedTest { jsonTestingMode ->
+ val json = Json {
+ serializersModule = module + SerializersModule {
+ polymorphicDefaultDeserializer(PolyBase::class) { name ->
+ if (name == null) {
+ PolyDefaultDeserializer
+ } else {
+ null
+ }
+ }
+ }
+ }
+ val string = """
+ {"polyBase1":{"type":"kotlinx.serialization.PolyBase","id":239},
+ "polyBase2":{"key":42}}""".trimIndent()
+ val result = json.decodeFromString(Wrapper.serializer(), string, jsonTestingMode)
+ assertEquals(Wrapper(PolyBase(239), PolyDefault(JsonObject(mapOf("key" to JsonPrimitive(42))))), result)
+ }
+
+ @Test
+ fun testDefaultSerializer() = parametrizedTest { jsonTestingMode ->
+ val json = Json {
+ serializersModule = module + SerializersModule {
+ polymorphicDefaultSerializer(PolyBase::class) { value ->
+ if (value.id % 2 == 0) {
+ EvenDefaultSerializer
+ } else {
+ OddDefaultSerializer
+ }
+ }
+ }
+ }
+ val obj = Wrapper(
+ PolyDefaultWithId(0),
+ PolyDefaultWithId(1)
+ )
+ val s = json.encodeToString(Wrapper.serializer(), obj, jsonTestingMode)
+ assertEquals("""{"polyBase1":{"type":"even","parity":"even"},"polyBase2":{"type":"odd","parity":"odd"}}""", s)
+ }
+
+ @Serializable
+ sealed class Conf {
+ @Serializable
+ @SerialName("empty")
+ object Empty : Conf() // default
+
+ @Serializable
+ @SerialName("simple")
+ data class Simple(val value: String) : Conf()
+ }
+
+ private val jsonForConf = Json {
+ isLenient = false
+ ignoreUnknownKeys = true
+ serializersModule = SerializersModule {
+ polymorphicDefaultDeserializer(Conf::class) { Conf.Empty.serializer() }
+ }
+ }
+
+ @Test
+ fun defaultSerializerWithEmptyBodyTest() = parametrizedTest { mode ->
+ assertEquals(Conf.Simple("123"), jsonForConf.decodeFromString<Conf>("""{"type": "simple", "value": "123"}""", mode))
+ assertEquals(Conf.Empty, jsonForConf.decodeFromString<Conf>("""{"type": "default"}""", mode))
+ assertEquals(Conf.Empty, jsonForConf.decodeFromString<Conf>("""{"unknown": "Meow"}""", mode))
+ assertEquals(Conf.Empty, jsonForConf.decodeFromString<Conf>("""{}""", mode))
+ }
+
+ @Test
+ fun testTypeKeysInLenientMode() = parametrizedTest { mode ->
+ val json = Json(jsonForConf) { isLenient = true }
+
+ assertEquals(Conf.Simple("123"), json.decodeFromString<Conf>("""{type: simple, value: 123}""", mode))
+ assertEquals(Conf.Empty, json.decodeFromString<Conf>("""{type: default}""", mode))
+ assertEquals(Conf.Empty, json.decodeFromString<Conf>("""{unknown: Meow}""", mode))
+ assertEquals(Conf.Empty, json.decodeFromString<Conf>("""{}""", mode))
+
+ }
+}