/* * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.serialization import kotlinx.serialization.descriptors.* import kotlinx.serialization.encoding.* import kotlinx.serialization.modules.* import org.junit.Test import kotlin.test.* // Serializable data class @Serializable data class Data( val value1: String, val value2: Int ) // Serializable data class with explicit companion object @Serializable data class DataExplicit( val value1: String, val value2: Int ) { companion object } // Regular (non-data) class with var properties @Serializable class Reg { var value1: String = "" var value2: Int = 0 } // Specify serializable names @Serializable data class Names( @SerialName("value1") val custom1: String, @SerialName("value2") val custom2: Int ) // Custom serializer @Serializable(with= CustomSerializer::class) data class Custom( val _value1: String, val _value2: Int ) @Suppress("NAME_SHADOWING") object CustomSerializer : KSerializer { override val descriptor = object : SerialDescriptor { override val serialName = "kotlinx.serialization.Custom" override val kind: SerialKind = StructureKind.CLASS override val elementsCount: Int get() = 2 override fun getElementName(index: Int) = when(index) { 0 -> "value1" 1 -> "value2" else -> "" } override fun getElementIndex(name: String) = when(name) { "value1" -> 0 "value2" -> 1 else -> -1 } override fun getElementAnnotations(index: Int): List = emptyList() override fun getElementDescriptor(index: Int): SerialDescriptor = fail("Should not be called") override fun isElementOptional(index: Int): Boolean = false } override fun serialize(encoder: Encoder, value: Custom) { val encoder = encoder.beginStructure(descriptor) encoder.encodeStringElement(descriptor, 0, value._value1) encoder.encodeIntElement(descriptor, 1, value._value2) encoder.endStructure(descriptor) } override fun deserialize(decoder: Decoder): Custom { val decoder = decoder.beginStructure(descriptor) if (decoder.decodeElementIndex(descriptor) != 0) throw java.lang.IllegalStateException() val value1 = decoder.decodeStringElement(descriptor, 0) if (decoder.decodeElementIndex(descriptor) != 1) throw java.lang.IllegalStateException() val value2 = decoder.decodeIntElement(descriptor, 1) if (decoder.decodeElementIndex(descriptor) != CompositeDecoder.DECODE_DONE) throw java.lang.IllegalStateException() decoder.endStructure(descriptor) return Custom(value1, value2) } } // External serializer // not Serializable !!! data class ExternalData( val value1: String, val value2: Int ) @Serializer(forClass= ExternalData::class) object ExternalSerializer // --------- tests and utils --------- class SerializeFlatTest() { @Test fun testData() { val out = Out("Data") out.encodeSerializableValue(serializer(), Data("s1", 42)) out.done() val inp = Inp("Data") val data = inp.decodeSerializableValue(serializer()) inp.done() assert(data.value1 == "s1" && data.value2 == 42) } @Test fun testDataExplicit() { val out = Out("DataExplicit") out.encodeSerializableValue(serializer(), DataExplicit("s1", 42)) out.done() val inp = Inp("DataExplicit") val data = inp.decodeSerializableValue(serializer()) inp.done() assert(data.value1 == "s1" && data.value2 == 42) } @Test fun testReg() { val out = Out("Reg") val reg = Reg() reg.value1 = "s1" reg.value2 = 42 out.encodeSerializableValue(serializer(), reg) out.done() val inp = Inp("Reg") val data = inp.decodeSerializableValue(serializer()) inp.done() assert(data.value1 == "s1" && data.value2 == 42) } @Test fun testNames() { val out = Out("Names") out.encodeSerializableValue(serializer(), Names("s1", 42)) out.done() val inp = Inp("Names") val data = inp.decodeSerializableValue(serializer()) inp.done() assert(data.custom1 == "s1" && data.custom2 == 42) } @Test fun testCustom() { val out = Out("Custom") out.encodeSerializableValue(CustomSerializer, Custom("s1", 42)) out.done() val inp = Inp("Custom") val data = inp.decodeSerializableValue(CustomSerializer) inp.done() assert(data._value1 == "s1" && data._value2 == 42) } @Test fun testExternalData() { val out = Out("ExternalData") out.encodeSerializableValue(ExternalSerializer, ExternalData("s1", 42)) out.done() val inp = Inp("ExternalData") val data = inp.decodeSerializableValue(ExternalSerializer) inp.done() assert(data.value1 == "s1" && data.value2 == 42) } companion object { fun fail(msg: String): Nothing = throw RuntimeException(msg) fun checkDesc(name: String, desc: SerialDescriptor) { if (desc.serialName != "kotlinx.serialization." + name) fail("checkDesc name $desc") if (desc.kind != StructureKind.CLASS) fail("checkDesc kind ${desc.kind}") if (desc.getElementName(0) != "value1") fail("checkDesc[0] $desc") if (desc.getElementName(1) != "value2") fail("checkDesc[1] $desc") } } class Out(private val name: String) : AbstractEncoder() { var step = 0 override val serializersModule: SerializersModule = EmptySerializersModule() override fun beginStructure( descriptor: SerialDescriptor ): CompositeEncoder { checkDesc(name, descriptor) if (step == 0) step++ else fail("@$step: beginStructure($descriptor)") return this } override fun encodeElement(descriptor: SerialDescriptor, index: Int): Boolean { checkDesc(name, descriptor) when (step) { 1 -> if (index == 0) { step++ return true } 3 -> if (index == 1) { step++ return true } } fail("@$step: encodeElement($descriptor, $index)") } override fun encodeString(value: String) { when (step) { 2 -> if (value == "s1") { step++ return } } fail("@$step: encodeString($value)") } override fun encodeInt(value: Int) { when (step) { 4 -> if (value == 42) { step++ return } } fail("@$step: decodeInt($value)") } override fun endStructure(descriptor: SerialDescriptor) { checkDesc(name, descriptor) if (step == 5) step++ else fail("@$step: endStructure($descriptor)") } fun done() { if (step != 6) fail("@$step: OUT FAIL") } } class Inp(private val name: String) : AbstractDecoder() { var step = 0 override val serializersModule: SerializersModule = EmptySerializersModule() override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder { checkDesc(name, descriptor) if (step == 0) step++ else fail("@$step: beginStructure($descriptor)") return this } override fun decodeElementIndex(descriptor: SerialDescriptor): Int { checkDesc(name, descriptor) when (step) { 1 -> { step++ return 0 } 3 -> { step++ return 1 } 5 -> { step++ return -1 } } fail("@$step: decodeElementIndex($descriptor)") } override fun decodeString(): String { when (step) { 2 -> { step++ return "s1" } } fail("@$step: decodeString()") } override fun decodeInt(): Int { when (step) { 4 -> { step++ return 42 } } fail("@$step: decodeInt()") } override fun endStructure(descriptor: SerialDescriptor) { checkDesc(name, descriptor) if (step == 6) step++ else fail("@$step: endStructure($descriptor)") } fun done() { if (step != 7) fail("@$step: INP FAIL") } } }