[Serialization] Reorganize module structure
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
|
||||
// WITH_STDLIB
|
||||
|
||||
// FILE: a.kt
|
||||
|
||||
package a
|
||||
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.descriptors.*
|
||||
import kotlinx.serialization.encoding.*
|
||||
|
||||
object MultiplyingIntSerializer : KSerializer<Int> {
|
||||
override val descriptor: SerialDescriptor
|
||||
get() = PrimitiveSerialDescriptor("MultiplyingInt", PrimitiveKind.INT)
|
||||
|
||||
override fun deserialize(decoder: Decoder): Int {
|
||||
return decoder.decodeInt() / 2
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, value: Int) {
|
||||
encoder.encodeInt(value * 2)
|
||||
}
|
||||
}
|
||||
|
||||
data class Cont(val i: Int)
|
||||
|
||||
object ContSerializer: KSerializer<Cont> {
|
||||
override fun deserialize(decoder: Decoder): Cont {
|
||||
return Cont(decoder.decodeInt())
|
||||
}
|
||||
|
||||
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("ContSerializer", PrimitiveKind.INT)
|
||||
|
||||
override fun serialize(encoder: Encoder, value: Cont) {
|
||||
encoder.encodeInt(value.i)
|
||||
}
|
||||
}
|
||||
|
||||
// FILE: test.kt
|
||||
|
||||
@file:UseContextualSerialization(Cont::class)
|
||||
@file:UseSerializers(MultiplyingIntSerializer::class)
|
||||
|
||||
package a
|
||||
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.json.*
|
||||
import kotlinx.serialization.modules.*
|
||||
|
||||
@Serializable
|
||||
class Holder(
|
||||
val i: Int,
|
||||
val c: Cont
|
||||
)
|
||||
|
||||
fun testOnFile(): String {
|
||||
val j = Json {
|
||||
serializersModule = SerializersModule {
|
||||
contextual(ContSerializer)
|
||||
}
|
||||
}
|
||||
val h = Holder(3, Cont(4))
|
||||
val str = j.encodeToString(
|
||||
Holder.serializer(),
|
||||
h
|
||||
)
|
||||
if ("""{"i":6,"c":4}""" != str) return str
|
||||
val decoded = j.decodeFromString(Holder.serializer(), str)
|
||||
if (decoded.i != h.i) return "i: ${decoded.i}"
|
||||
if (decoded.c.i != h.c.i) return "c.i: ${decoded.c.i}"
|
||||
return "OK"
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
return testOnFile()
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
|
||||
// WITH_STDLIB
|
||||
|
||||
package a
|
||||
|
||||
import kotlinx.serialization.*
|
||||
|
||||
import kotlinx.serialization.descriptors.*
|
||||
import kotlinx.serialization.encoding.*
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class Dummy
|
||||
|
||||
class DummyBox<T>
|
||||
|
||||
@Serializable(ClassSerializerOnClass::class)
|
||||
class DummySpecified
|
||||
|
||||
class ClassSerializerGeneric : KSerializer<DummyBox<String>> {
|
||||
override val descriptor get() = PrimitiveSerialDescriptor("ClassSerializerGeneric", PrimitiveKind.INT)
|
||||
override fun deserialize(decoder: Decoder): DummyBox<String> = TODO()
|
||||
override fun serialize(encoder: Encoder, value:DummyBox<String>): Unit = TODO()
|
||||
}
|
||||
|
||||
class ClassSerializerDummy : KSerializer<Dummy> {
|
||||
override val descriptor get() = PrimitiveSerialDescriptor("ClassSerializerDummy", PrimitiveKind.INT)
|
||||
override fun deserialize(decoder: Decoder): Dummy = TODO()
|
||||
override fun serialize(encoder: Encoder, value: Dummy): Unit = TODO()
|
||||
}
|
||||
|
||||
object ObjectSerializerGeneric: KSerializer<DummyBox<String>> {
|
||||
override val descriptor get() = PrimitiveSerialDescriptor("ObjectSerializerGeneric", PrimitiveKind.INT)
|
||||
override fun deserialize(decoder: Decoder): DummyBox<String> = TODO()
|
||||
override fun serialize(encoder: Encoder, value: DummyBox<String>): Unit = TODO()
|
||||
}
|
||||
|
||||
object ObjectSerializerDummy: KSerializer<Dummy> {
|
||||
override val descriptor get() = PrimitiveSerialDescriptor("ObjectSerializerDummy", PrimitiveKind.INT)
|
||||
override fun deserialize(decoder: Decoder): Dummy = TODO()
|
||||
override fun serialize(encoder: Encoder, value:Dummy): Unit = TODO()
|
||||
}
|
||||
|
||||
class ClassSerializerOnClass: KSerializer<DummySpecified> {
|
||||
override val descriptor get() = PrimitiveSerialDescriptor("ClassSerializerOnClass", PrimitiveKind.INT)
|
||||
override fun deserialize(decoder: Decoder): DummySpecified = TODO()
|
||||
override fun serialize(encoder: Encoder, value:DummySpecified): Unit = TODO()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
class Holder(
|
||||
@Serializable(ClassSerializerGeneric::class) val a: DummyBox<String>,
|
||||
@Serializable(ClassSerializerDummy::class) val b: Dummy,
|
||||
@Serializable(ObjectSerializerGeneric::class) val c: DummyBox<String>,
|
||||
@Serializable(ObjectSerializerDummy::class) val d: Dummy,
|
||||
val e: DummySpecified
|
||||
)
|
||||
|
||||
fun box(): String {
|
||||
val descs = Holder.serializer().descriptor.elementDescriptors.toList()
|
||||
assertEquals("ClassSerializerGeneric", descs[0].serialName)
|
||||
assertEquals("ClassSerializerDummy", descs[1].serialName)
|
||||
assertEquals("ObjectSerializerGeneric", descs[2].serialName)
|
||||
assertEquals("ObjectSerializerDummy", descs[3].serialName)
|
||||
assertEquals("ClassSerializerOnClass", descs[4].serialName)
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
|
||||
// WITH_STDLIB
|
||||
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.json.*
|
||||
import kotlinx.serialization.internal.*
|
||||
|
||||
enum class Plain {
|
||||
A, B
|
||||
}
|
||||
|
||||
@Serializable enum class WithNames {
|
||||
@SerialName("A") ENTRY1,
|
||||
@SerialName("B") ENTRY2
|
||||
}
|
||||
|
||||
@Serializable
|
||||
class Holder(val p: Plain, val w: WithNames)
|
||||
|
||||
@OptIn(InternalSerializationApi::class)
|
||||
fun testSerializers(): String {
|
||||
val cs = (Holder.serializer() as GeneratedSerializer<*>).childSerializers()
|
||||
val str1 = cs[0].toString()
|
||||
if (!str1.contains("kotlinx.serialization.internal.EnumSerializer")) return str1
|
||||
|
||||
/**
|
||||
* Serialization 1.4.1+ have runtime factories to create EnumSerializer instead of synthetic $serializer, saving bytecode
|
||||
* and bringing consistency. After updating the version, uncomment this block.
|
||||
*/
|
||||
// val str2 = cs[1].toString()
|
||||
// if (!str2.contains("kotlinx.serialization.internal.EnumSerializer")) return str2
|
||||
return "OK"
|
||||
}
|
||||
|
||||
fun testSerialization(previous: String): String {
|
||||
if (previous != "OK") return previous
|
||||
val h = Holder(Plain.B, WithNames.ENTRY1)
|
||||
val s = Json.encodeToString(Holder.serializer(), h)
|
||||
if (s != """{"p":"B","w":"A"}""") return s
|
||||
if (Json.decodeFromString(Holder.serializer(), s).w != WithNames.ENTRY1) return "Deserialization failure"
|
||||
return "OK"
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
return testSerialization(testSerializers())
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
|
||||
// WITH_STDLIB
|
||||
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.json.*
|
||||
|
||||
@Serializable
|
||||
data class Foo<T>(val i: Int, val t: T? = null)
|
||||
|
||||
@Serializable
|
||||
class Holder(val f1: Foo<String>, val f2: Foo<Int>)
|
||||
|
||||
fun box(): String {
|
||||
val holder = Holder(Foo(1, "1"), Foo(2))
|
||||
val str = Json.encodeToString(Holder.serializer(), holder)
|
||||
if (str != """{"f1":{"i":1,"t":"1"},"f2":{"i":2}}""") return str
|
||||
val decoded = Json.decodeFromString(Holder.serializer(), str)
|
||||
if (decoded.f1.t != holder.f1.t) return "f1.t: ${decoded.f1.t}"
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
|
||||
// WITH_STDLIB
|
||||
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.json.*
|
||||
import kotlinx.serialization.internal.*
|
||||
|
||||
@Serializable
|
||||
@JvmInline
|
||||
value class Foo(val i: Int)
|
||||
|
||||
@Serializable
|
||||
class Holder(val f: Foo)
|
||||
|
||||
fun box(): String {
|
||||
if(!Foo.serializer().descriptor.isInline) return "Incorrect descriptor"
|
||||
val s = Json.encodeToString(Holder.serializer(), Holder(Foo(42)))
|
||||
if (s != """{"f":42}""") return s
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
|
||||
// WITH_STDLIB
|
||||
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.encoding.*
|
||||
import kotlinx.serialization.descriptors.*
|
||||
import kotlinx.serialization.json.*
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.test.*
|
||||
|
||||
// TODO: for this test to work, runtime dependency should be updated to (yet unreleased) serialization with @MetaSerializable annotation
|
||||
|
||||
@MetaSerializable
|
||||
@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY)
|
||||
annotation class MySerializable
|
||||
|
||||
@MetaSerializable
|
||||
@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY)
|
||||
annotation class MySerializableWithInfo(
|
||||
val value: Int,
|
||||
val kclass: KClass<*>
|
||||
)
|
||||
|
||||
@MySerializable
|
||||
class Project1(val name: String, val language: String)
|
||||
|
||||
@MySerializableWithInfo(123, String::class)
|
||||
class Project2(val name: String, val language: String)
|
||||
|
||||
@Serializable
|
||||
class Wrapper(
|
||||
@MySerializableWithInfo(234, Int::class) val project: Project2
|
||||
)
|
||||
|
||||
@Serializable
|
||||
@MySerializableWithInfo(123, String::class)
|
||||
class Project3(val name: String, val language: String)
|
||||
|
||||
@Serializable(with = MySerializer::class)
|
||||
@MySerializableWithInfo(123, String::class)
|
||||
class Project4(val name: String, val language: String)
|
||||
|
||||
@MySerializableWithInfo(123, String::class)
|
||||
sealed class TestSealed {
|
||||
@MySerializableWithInfo(123, String::class)
|
||||
class A(val value1: String) : TestSealed()
|
||||
@MySerializableWithInfo(123, String::class)
|
||||
class B(val value2: String) : TestSealed()
|
||||
}
|
||||
|
||||
@MySerializable
|
||||
abstract class TestAbstract {
|
||||
@MySerializableWithInfo(123, String::class)
|
||||
class A(val value1: String) : TestSealed()
|
||||
|
||||
@MySerializableWithInfo(123, String::class)
|
||||
class B(val value2: String) : TestSealed()
|
||||
}
|
||||
|
||||
@MySerializableWithInfo(123, String::class)
|
||||
enum class TestEnum { Value1, Value2 }
|
||||
|
||||
@MySerializableWithInfo(123, String::class)
|
||||
object TestObject
|
||||
|
||||
object MySerializer : KSerializer<Project4> {
|
||||
|
||||
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Project4", PrimitiveKind.STRING)
|
||||
|
||||
override fun serialize(encoder: Encoder, value: Project4) = encoder.encodeString("${value.name}:${value.language}")
|
||||
|
||||
override fun deserialize(decoder: Decoder): Project4 {
|
||||
val params = decoder.decodeString().split(':')
|
||||
return Project4(params[0], params[1])
|
||||
}
|
||||
}
|
||||
|
||||
fun testMetaSerializable() {
|
||||
val string = Json.encodeToString(Project1.serializer(), Project1("name", "lang"))
|
||||
assertEquals("""{"name":"name","language":"lang"}""", string)
|
||||
|
||||
val reconstructed = Json.decodeFromString(Project1.serializer(), string)
|
||||
assertEquals("name", reconstructed.name)
|
||||
assertEquals("lang", reconstructed.language)
|
||||
}
|
||||
|
||||
fun testMetaSerializableWithInfo() {
|
||||
val string = Json.encodeToString(Project2.serializer(), Project2("name", "lang"))
|
||||
assertEquals("""{"name":"name","language":"lang"}""", string)
|
||||
|
||||
val reconstructed = Json.decodeFromString(Project2.serializer(), string)
|
||||
assertEquals("name", reconstructed.name)
|
||||
assertEquals("lang", reconstructed.language)
|
||||
|
||||
val info = Project2.serializer().descriptor.annotations.filterIsInstance<MySerializableWithInfo>().first()
|
||||
assertEquals(123, info.value)
|
||||
assertEquals(String::class, info.kclass)
|
||||
}
|
||||
|
||||
fun testMetaSerializableOnProperty() {
|
||||
val info = Wrapper.serializer().descriptor.getElementAnnotations(0).filterIsInstance<MySerializableWithInfo>().first()
|
||||
assertEquals(234, info.value)
|
||||
assertEquals(Int::class, info.kclass)
|
||||
}
|
||||
|
||||
fun testSerializableAndMetaAnnotation() {
|
||||
val string = Json.encodeToString(Project3.serializer(), Project3("name", "lang"))
|
||||
assertEquals("""{"name":"name","language":"lang"}""", string)
|
||||
|
||||
val reconstructed = Json.decodeFromString(Project3.serializer(), string)
|
||||
assertEquals("name", reconstructed.name)
|
||||
assertEquals("lang", reconstructed.language)
|
||||
|
||||
val info = Project3.serializer().descriptor.annotations.filterIsInstance<MySerializableWithInfo>().first()
|
||||
assertEquals(123, info.value)
|
||||
assertEquals(String::class, info.kclass)
|
||||
}
|
||||
|
||||
fun testCustomSerializerAndMetaAnnotation() {
|
||||
val string = Json.encodeToString(Project4.serializer(), Project4("name", "lang"))
|
||||
assertEquals("""name:lang""", string)
|
||||
|
||||
val reconstructed = Json.decodeFromString(Project4.serializer(), string)
|
||||
assertEquals("name", reconstructed.name)
|
||||
assertEquals("lang", reconstructed.language)
|
||||
}
|
||||
|
||||
fun testSealed() {
|
||||
val serializerA = TestSealed.A.serializer()
|
||||
val serializerB = TestSealed.B.serializer()
|
||||
assertNotNull(serializerA)
|
||||
assertNotNull(serializerB)
|
||||
|
||||
val infoA = serializerA.descriptor.annotations.filterIsInstance<MySerializableWithInfo>().first()
|
||||
val infoB = serializerB.descriptor.annotations.filterIsInstance<MySerializableWithInfo>().first()
|
||||
assertEquals(123, infoA.value)
|
||||
assertEquals(String::class, infoA.kclass)
|
||||
assertEquals(123, infoB.value)
|
||||
assertEquals(String::class, infoB.kclass)
|
||||
}
|
||||
|
||||
fun testAbstract() {
|
||||
val serializerA = TestAbstract.A.serializer()
|
||||
val serializerB = TestAbstract.B.serializer()
|
||||
assertNotNull(serializerA)
|
||||
assertNotNull(serializerB)
|
||||
|
||||
val infoA = serializerA.descriptor.annotations.filterIsInstance<MySerializableWithInfo>().first()
|
||||
val infoB = serializerB.descriptor.annotations.filterIsInstance<MySerializableWithInfo>().first()
|
||||
assertEquals(123, infoA.value)
|
||||
assertEquals(String::class, infoA.kclass)
|
||||
assertEquals(123, infoB.value)
|
||||
assertEquals(String::class, infoB.kclass)
|
||||
}
|
||||
|
||||
fun testEnum() {
|
||||
val serializer = TestEnum.serializer()
|
||||
assertNotNull(serializer)
|
||||
|
||||
val info = serializer.descriptor.annotations.filterIsInstance<MySerializableWithInfo>().first()
|
||||
assertEquals(123, info.value)
|
||||
assertEquals(String::class, info.kclass)
|
||||
}
|
||||
|
||||
fun testObject() {
|
||||
val serializer = TestObject.serializer()
|
||||
assertNotNull(serializer)
|
||||
|
||||
val info = serializer.descriptor.annotations.filterIsInstance<MySerializableWithInfo>().first()
|
||||
assertEquals(123, info.value)
|
||||
assertEquals(String::class, info.kclass)
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
testMetaSerializable()
|
||||
testMetaSerializableWithInfo()
|
||||
testMetaSerializableOnProperty()
|
||||
testSealed()
|
||||
testAbstract()
|
||||
testEnum()
|
||||
testObject()
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
|
||||
// WITH_STDLIB
|
||||
|
||||
// MODULE: lib
|
||||
// FILE: lib.kt
|
||||
|
||||
package a
|
||||
|
||||
import kotlinx.serialization.*
|
||||
|
||||
@Serializable
|
||||
open class OpenBody {
|
||||
var optional: String? = "foo"
|
||||
}
|
||||
|
||||
@Serializable
|
||||
abstract class AbstractConstructor(var optional: String = "foo")
|
||||
|
||||
|
||||
// MODULE: app(lib)
|
||||
// FILE: app.kt
|
||||
|
||||
package test
|
||||
|
||||
import a.*
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.json.*
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@Serializable
|
||||
class Test1: OpenBody()
|
||||
|
||||
@Serializable
|
||||
class Test2: AbstractConstructor()
|
||||
|
||||
fun test1() {
|
||||
val string = Json.encodeToString(Test1.serializer(), Test1())
|
||||
assertEquals("{}", string)
|
||||
val reconstructed = Json.decodeFromString(Test1.serializer(), string)
|
||||
assertEquals("foo", reconstructed.optional)
|
||||
}
|
||||
|
||||
fun test2() {
|
||||
val string = Json.encodeToString(Test2.serializer(), Test2())
|
||||
assertEquals("{}", string)
|
||||
val reconstructed = Json.decodeFromString(Test2.serializer(), string)
|
||||
assertEquals("foo", reconstructed.optional)
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
test1()
|
||||
test2()
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
|
||||
// WITH_STDLIB
|
||||
|
||||
package a
|
||||
|
||||
import kotlinx.serialization.*
|
||||
|
||||
import kotlinx.serialization.descriptors.*
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
interface I
|
||||
|
||||
sealed interface SI
|
||||
|
||||
@Serializable
|
||||
sealed interface SSI
|
||||
|
||||
@Serializable
|
||||
class Holder(
|
||||
val i: I,
|
||||
val si: SI,
|
||||
val ssi: SSI
|
||||
)
|
||||
|
||||
fun SerialDescriptor.checkKind(index: Int, kind: String) {
|
||||
assertEquals(kind, getElementDescriptor(index).kind.toString())
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val desc = Holder.serializer().descriptor
|
||||
desc.checkKind(0, "OPEN")
|
||||
desc.checkKind(1, "OPEN")
|
||||
desc.checkKind(2, "SEALED")
|
||||
return "OK"
|
||||
}
|
||||
Reference in New Issue
Block a user