/* * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.serialization import kotlinx.serialization.builtins.* import kotlinx.serialization.descriptors.* import kotlinx.serialization.encoding.* import kotlinx.serialization.features.sealed.SealedChild import kotlinx.serialization.features.sealed.SealedParent import kotlinx.serialization.json.* import kotlinx.serialization.modules.* import kotlinx.serialization.test.* import kotlin.reflect.* import kotlin.test.* import kotlin.time.Duration @Suppress("RemoveExplicitTypeArguments") // This is exactly what's being tested class SerializersLookupTest : JsonTestBase() { @Test fun testPrimitive() { val token = typeOf() val serial = serializer(token) assertSame(Int.serializer() as KSerializer<*>, serial) assertSerializedWithType("42", 42) } @Test fun testPlainClass() { val b = StringData("some string") assertSerializedWithType("""{"data":"some string"}""", b) } @Test fun testListWithT() { val source = """[{"intV":42}]""" val serial = serializer>() assertEquals(listOf(IntData(42)), Json.decodeFromString(serial, source)) } @Test fun testPrimitiveList() { val myArr = listOf("a", "b", "c") assertSerializedWithType("""["a","b","c"]""", myArr) } @Test fun testListAsCollection() { val myArr: Collection = listOf("a", "b", "c") assertSerializedWithType("""["a","b","c"]""", myArr) } @Test fun testUnsigned() { assertSame(UByte.serializer(), serializer()) assertSame(UShort.serializer(), serializer()) assertSame(UInt.serializer(), serializer()) assertSame(ULong.serializer(), serializer()) } @Test @OptIn(ExperimentalUnsignedTypes::class) fun testUnsignedArrays() { assertSame(UByteArraySerializer(), serializer()) assertSame(UShortArraySerializer(), serializer()) assertSame(UIntArraySerializer(), serializer()) assertSame(ULongArraySerializer(), serializer()) } @Test fun testPrimitiveSet() { val mySet = setOf("a", "b", "c", "c") assertSerializedWithType("""["a","b","c"]""", mySet) } @Test fun testMapWithT() { val myMap = mapOf("string" to StringData("foo"), "string2" to StringData("bar")) assertSerializedWithType("""{"string":{"data":"foo"},"string2":{"data":"bar"}}""", myMap) } @Test fun testNestedLists() { val myList = listOf(listOf(listOf(1, 2, 3)), listOf()) assertSerializedWithType("[[[1,2,3]],[]]", myList) } @Test fun testListSubtype() { val myList = arrayListOf(1, 2, 3) assertSerializedWithType>("[1,2,3]", myList) assertSerializedWithType>("[1,2,3]", myList) } @Test fun testListProjection() { val myList = arrayListOf(1, 2, 3) assertSerializedWithType>("[1,2,3]", myList) assertSerializedWithType>("[1,2,3]", myList) assertSerializedWithType>("[1,2,3]", myList) } @Test fun testStarProjectionsAreProhibited() { val expectedMessage = "Star projections in type arguments are not allowed" assertFailsWithMessage(expectedMessage) { serializer>() } assertFailsWithMessage(expectedMessage) { serializer(typeOf>()) } assertFailsWithMessage(expectedMessage) { serializerOrNull(typeOf>()) } } @Test fun testNullableTypes() { val myList: List = listOf(1, null, 3) assertSerializedWithType("[1,null,3]", myList) assertSerializedWithType?>("[1,null,3]", myList) } @Test fun testPair() { val myPair = "42" to 42 assertSerializedWithType("""{"first":"42","second":42}""", myPair) } @Test fun testTriple() { val myTriple = Triple("1", 2, Box(42)) assertSerializedWithType("""{"first":"1","second":2,"third":{"boxed":42}}""", myTriple) } @Test fun testLookupDuration() { assertNotNull(serializerOrNull(typeOf())) assertSame(Duration.serializer(), serializer()) } @Test fun testCustomGeneric() { val intBox = Box(42) val intBoxSerializer = serializer>() assertEquals(Box.serializer(Int.serializer()).descriptor, intBoxSerializer.descriptor) assertSerializedWithType("""{"boxed":42}""", intBox) val dataBox = Box(StringData("foo")) assertSerializedWithType("""{"boxed":{"data":"foo"}}""", dataBox) } @Test fun testRecursiveGeneric() { val boxBox = Box(Box(Box(IntData(42)))) assertSerializedWithType("""{"boxed":{"boxed":{"boxed":{"intV":42}}}}""", boxBox) } @Test fun testMixedGeneric() { val listOfBoxes = listOf(Box("foo"), Box("bar")) assertSerializedWithType("""[{"boxed":"foo"},{"boxed":"bar"}]""", listOfBoxes) val boxedList = Box(listOf("foo", "bar")) assertSerializedWithType("""{"boxed":["foo","bar"]}""", boxedList) } @Test fun testReferenceArrays() { assertSerializedWithType("[1,2,3]", Array(3) { it + 1 }, default) assertSerializedWithType("""["1","2","3"]""", Array(3) { (it + 1).toString() }, default) assertSerializedWithType("[[0],[1],[2]]", Array>(3) { cnt -> Array(1) { cnt } }, default) assertSerializedWithType("""[{"boxed":"foo"}]""", Array(1) { Box("foo") }, default) assertSerializedWithType("""[[{"boxed":"foo"}]]""", Array(1) { Array(1) { Box("foo") } }, default) } @Test fun testPrimitiveArrays() { assertSerializedWithType("[1,2,3]", intArrayOf(1, 2, 3), default) assertSerializedWithType("[1,2,3]", longArrayOf(1, 2, 3), default) assertSerializedWithType("[1,2,3]", byteArrayOf(1, 2, 3), default) assertSerializedWithType("[1,2,3]", shortArrayOf(1, 2, 3), default) assertSerializedWithType("[true,false]", booleanArrayOf(true, false), default) assertSerializedWithType("""["a","b","c"]""", charArrayOf('a', 'b', 'c'), default) } @Test fun testSerializableObject() { assertSerializedWithType("{}", SampleObject) } class IntBox(val i: Int) class CustomIntSerializer(isNullable: Boolean) : KSerializer { override val descriptor: SerialDescriptor init { val d = PrimitiveSerialDescriptor("CIS", PrimitiveKind.INT) descriptor = if (isNullable) d.nullable else d } override fun serialize(encoder: Encoder, value: IntBox?) { if (value == null) encoder.encodeInt(41) else encoder.encodeInt(42) } override fun deserialize(decoder: Decoder): IntBox? { TODO() } } class GenericHolder(value: T) class GenericSerializer(typeSerializer: KSerializer) : KSerializer> { override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor( "Generic Serializer parametrized by ${typeSerializer.descriptor}", PrimitiveKind.STRING ) override fun deserialize(decoder: Decoder): GenericHolder { TODO() } override fun serialize(encoder: Encoder, value: GenericHolder) { TODO() } } @Test fun testContextualLookup() { val module = SerializersModule { contextual(CustomIntSerializer(false).cast()) } val json = Json { serializersModule = module } val data = listOf(listOf(IntBox(1))) assertEquals("[[42]]", json.encodeToString(data)) } @Test fun testGenericOfContextual() { val module = SerializersModule { contextual(CustomIntSerializer(false).cast()) contextual(GenericHolder::class) { args -> GenericSerializer(args[0]) } } val listSerializer = module.serializerOrNull(typeOf>()) assertNotNull(listSerializer) assertEquals("kotlin.collections.ArrayList(PrimitiveDescriptor(CIS))", listSerializer.descriptor.toString()) val genericSerializer = module.serializerOrNull(typeOf>()) assertNotNull(genericSerializer) assertEquals( "PrimitiveDescriptor(Generic Serializer parametrized by PrimitiveDescriptor(CIS))", genericSerializer.descriptor.toString() ) } @Test fun testContextualLookupNullable() { val module = SerializersModule { contextual(CustomIntSerializer(true).cast()) } val serializer = module.serializer>>() assertEquals("[[41]]", Json.encodeToString(serializer, listOf(listOf(null)))) } @Test fun testContextualLookupNonNullable() { val module = SerializersModule { contextual(CustomIntSerializer(false).cast()) } val serializer = module.serializer>>() assertEquals("[[null]]", Json.encodeToString(serializer, listOf(listOf(null)))) } @Test fun testCompiledWinsOverContextual() { val contextual = object : KSerializer { override val descriptor: SerialDescriptor = Int.serializer().descriptor override fun serialize(encoder: Encoder, value: Int) { fail() } override fun deserialize(decoder: Decoder): Int { fail() } } val json = Json { serializersModule = SerializersModule { contextual(contextual) } } assertEquals("[[1]]", json.encodeToString(listOf(listOf(1)))) assertEquals("42", json.encodeToString(42)) } class NonSerializable class NonSerializableBox(val boxed: T) @Test fun testSealedFromOtherFileLookup() { assertNotNull(serializerOrNull(typeOf())) assertNotNull(serializerOrNull(typeOf())) } @Test fun testLookupFail() { assertNull(serializerOrNull(typeOf())) assertNull(serializerOrNull(typeOf>())) assertNull(serializerOrNull(typeOf>())) assertFailsWithMessage("for class 'NonSerializable'") { serializer(typeOf()) } assertFailsWithMessage("for class 'NonSerializableBox'") { serializer(typeOf>()) } assertFailsWithMessage("for class 'NonSerializable'") { serializer(typeOf>()) } } private inline fun assertSerializedWithType( expected: String, value: T, json: StringFormat = default ) { val serial = serializer() assertEquals(expected, json.encodeToString(serial, value)) val serial2 = requireNotNull(serializerOrNull(typeOf())) { "Expected serializer to be found" } assertEquals(expected, json.encodeToString(serial2, value)) } @Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE") inline fun KSerializer<*>.cast(): KSerializer = this as KSerializer }