diff --git a/CHANGELOG.md b/CHANGELOG.md index dd291332caf..4a48bea34b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.4.35 + +* `Versions`: + * `Kotlin Exposed`: `0.30.1` -> `0.30.2` +* `Serialization`: + * `TypedSerializer`: + * Project has been inited + ## 0.4.34 * `Versions`: diff --git a/gradle.properties b/gradle.properties index 63ac75bd490..529e93a4469 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ android.enableJetifier=true kotlin_version=1.4.32 kotlin_coroutines_version=1.4.3 kotlin_serialisation_core_version=1.1.0 -kotlin_exposed_version=0.30.1 +kotlin_exposed_version=0.30.2 ktor_version=1.5.3 @@ -44,5 +44,5 @@ dokka_version=1.4.30 # Project data group=dev.inmo -version=0.4.34 -android_code_version=38 +version=0.4.35 +android_code_version=39 diff --git a/serialization/typed_serializer/build.gradle b/serialization/typed_serializer/build.gradle new file mode 100644 index 00000000000..7c54502f100 --- /dev/null +++ b/serialization/typed_serializer/build.gradle @@ -0,0 +1,7 @@ +plugins { + id "org.jetbrains.kotlin.multiplatform" + id "org.jetbrains.kotlin.plugin.serialization" + id "com.android.library" +} + +apply from: "$mppProjectWithSerializationPresetPath" diff --git a/serialization/typed_serializer/src/commonMain/kotlin/dev/inmo/micro_utils/serialization/typed_serializer/TypedSerializer.kt b/serialization/typed_serializer/src/commonMain/kotlin/dev/inmo/micro_utils/serialization/typed_serializer/TypedSerializer.kt new file mode 100644 index 00000000000..02a486a957e --- /dev/null +++ b/serialization/typed_serializer/src/commonMain/kotlin/dev/inmo/micro_utils/serialization/typed_serializer/TypedSerializer.kt @@ -0,0 +1,76 @@ +package dev.inmo.micro_utils.serialization.typed_serializer + +import kotlinx.serialization.* +import kotlinx.serialization.builtins.serializer +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlin.reflect.KClass + +class TypedSerializer( + kClass: KClass, + presetSerializers: Map> = emptyMap() +) : KSerializer { + private val serializers = presetSerializers.toMutableMap() + @InternalSerializationApi + override val descriptor: SerialDescriptor = buildSerialDescriptor( + "TextSourceSerializer", + SerialKind.CONTEXTUAL + ) { + element("type", String.serializer().descriptor) + element("value", ContextualSerializer(kClass).descriptor) + } + + @InternalSerializationApi + override fun deserialize(decoder: Decoder): T { + return decoder.decodeStructure(descriptor) { + var type: String? = null + lateinit var result: T + while (true) { + when (val index = decodeElementIndex(descriptor)) { + 0 -> type = decodeStringElement(descriptor, 0) + 1 -> { + require(type != null) { "Type is null, but it is expected that was inited already" } + result = decodeSerializableElement( + descriptor, + 1, + serializers.getValue(type) + ) + } + CompositeDecoder.DECODE_DONE -> break + else -> error("Unexpected index: $index") + } + } + result + } + } + + @InternalSerializationApi + private fun CompositeEncoder.encode(value: O) { + encodeSerializableElement(descriptor, 1, value::class.serializer() as KSerializer, value) + } + + @InternalSerializationApi + override fun serialize(encoder: Encoder, value: T) { + encoder.encodeStructure(descriptor) { + val valueSerializer = value::class.serializer() + val type = serializers.keys.first { serializers[it] == valueSerializer } + encodeStringElement(descriptor, 0, type) + encode(value) + } + } + + + fun include(type: String, serializer: KSerializer) { + require(type !in serializers.keys) + serializers[type] = serializer + } + + fun exclude(type: String) { + require(type !in serializers.keys) + serializers.remove(type) + } +} + +inline fun TypedSerializer( + presetSerializers: Map> = emptyMap() +) = TypedSerializer(T::class, presetSerializers) diff --git a/serialization/typed_serializer/src/commonTest/kotlin/dev/inmo/micro_utils/serialization/typed_serializer/TypedSerializerTests.kt b/serialization/typed_serializer/src/commonTest/kotlin/dev/inmo/micro_utils/serialization/typed_serializer/TypedSerializerTests.kt new file mode 100644 index 00000000000..cf14169be80 --- /dev/null +++ b/serialization/typed_serializer/src/commonTest/kotlin/dev/inmo/micro_utils/serialization/typed_serializer/TypedSerializerTests.kt @@ -0,0 +1,40 @@ +package dev.inmo.micro_utils.serialization.typed_serializer + +import kotlinx.serialization.Serializable +import kotlinx.serialization.builtins.ListSerializer +import kotlinx.serialization.json.Json +import kotlin.random.Random +import kotlin.test.Test +import kotlin.test.assertEquals + +class TypedSerializerTests { + interface Example { + val number: Number + } + val serialFormat = Json { } + + @Serializable + data class Example1(override val number: Long) : Example + + @Serializable + data class Example2(override val number: Double) : Example + + @Test + fun testThatSerializerWorksCorrectly() { + val serializer = TypedSerializer( + mapOf( + "long" to Example1.serializer(), + "double" to Example2.serializer() + ) + ) + + val value1 = Example1(Random.nextLong()) + val value2 = Example2(Random.nextDouble()) + + val list = listOf(value1, value2) + val serialized = serialFormat.encodeToString(ListSerializer(serializer), list) + val deserialized = serialFormat.decodeFromString(ListSerializer(serializer), serialized) + + assertEquals(list, deserialized) + } +} \ No newline at end of file diff --git a/serialization/typed_serializer/src/main/AndroidManifest.xml b/serialization/typed_serializer/src/main/AndroidManifest.xml new file mode 100644 index 00000000000..7af0107d187 --- /dev/null +++ b/serialization/typed_serializer/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 5d0afc1c814..1cdf43a6d64 100644 --- a/settings.gradle +++ b/settings.gradle @@ -26,6 +26,7 @@ String[] includes = [ ":android:alerts:recyclerview", ":serialization:base64", ":serialization:encapsulator", + ":serialization:typed_serializer", ":dokka" ]