diff --git a/CHANGELOG.md b/CHANGELOG.md index 18fcd70..c6da894 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # SDI changelogs +## 0.3.0 + +* Versions: + * `Kotlin`: `1.3.70` -> `1.3.71` +* Common: + * Now it is possible to pass own lambda with receiver `SerializersModuleBuilder` to customize deserialization + * Now it will correctly resolve objects which was not previously registered + * By default, for modules loading will be used context from `Json`, passed as receiver + ## 0.2.0 * `Kotlin`: `1.3.61` -> `1.3.70` diff --git a/build.gradle b/build.gradle index 694f5d8..023da18 100644 --- a/build.gradle +++ b/build.gradle @@ -17,7 +17,7 @@ plugins { id "org.jetbrains.kotlin.plugin.serialization" version "$kotlin_version" } -project.version = "0.2.0" +project.version = "0.3.0" project.group = "com.insanusmokrassar" apply from: "publish.gradle" diff --git a/gradle.properties b/gradle.properties index 32a449c..1de97a5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ kotlin.code.style=official -kotlin_version=1.3.70 +kotlin_version=1.3.71 kotlin_serialisation_runtime_version=0.20.0 gradle_bintray_plugin_version=1.8.4 diff --git a/src/commonMain/kotlin/com/insanusmokrassar/sdi/HelperTools.kt b/src/commonMain/kotlin/com/insanusmokrassar/sdi/HelperTools.kt index f60ec5d..dd4c591 100644 --- a/src/commonMain/kotlin/com/insanusmokrassar/sdi/HelperTools.kt +++ b/src/commonMain/kotlin/com/insanusmokrassar/sdi/HelperTools.kt @@ -2,16 +2,25 @@ package com.insanusmokrassar.sdi import kotlinx.serialization.ImplicitReflectionSerializer import kotlinx.serialization.json.Json +import kotlinx.serialization.modules.SerializersModuleBuilder internal val nonStrictJson = Json { isLenient = true ignoreUnknownKeys = true serializeSpecialFloatingPointValues = true - useArrayPolymorphism = true } @ImplicitReflectionSerializer -fun Json.loadModule(json: String): Module = parse(ModuleDeserializerStrategy, json) +private val ModuleDeserializerStrategyWithNullOptional = ModuleDeserializerStrategy() @ImplicitReflectionSerializer -fun loadModule(json: String): Module = nonStrictJson.loadModule(json) +fun Json.loadModule( + json: String, + moduleBuilder: (SerializersModuleBuilder.() -> Unit)? = null +): Module = parse(if (moduleBuilder != null) ModuleDeserializerStrategy(moduleBuilder) else ModuleDeserializerStrategyWithNullOptional, json) + +@ImplicitReflectionSerializer +fun loadModule( + json: String, + moduleBuilder: (SerializersModuleBuilder.() -> Unit)? = null +): Module = nonStrictJson.loadModule(json, moduleBuilder) diff --git a/src/commonMain/kotlin/com/insanusmokrassar/sdi/ModuleDeserializerStrategy.kt b/src/commonMain/kotlin/com/insanusmokrassar/sdi/ModuleDeserializerStrategy.kt index 301d3e7..33b0aea 100644 --- a/src/commonMain/kotlin/com/insanusmokrassar/sdi/ModuleDeserializerStrategy.kt +++ b/src/commonMain/kotlin/com/insanusmokrassar/sdi/ModuleDeserializerStrategy.kt @@ -5,17 +5,19 @@ import kotlinx.serialization.* import kotlinx.serialization.builtins.MapSerializer import kotlinx.serialization.builtins.serializer import kotlinx.serialization.json.* +import kotlinx.serialization.modules.SerializersModuleBuilder @ImplicitReflectionSerializer -internal object ModuleDeserializerStrategy : DeserializationStrategy { +internal class ModuleDeserializerStrategy( + private val moduleBuilder: (SerializersModuleBuilder.() -> Unit)? = null +) : DeserializationStrategy { private val internalSerializer = MapSerializer(String.serializer(), ContextSerializer(Any::class)) override val descriptor: SerialDescriptor get() = internalSerializer.descriptor - @InternalSerializationApi override fun deserialize(decoder: Decoder): Module { val json = JsonObjectSerializer.deserialize(decoder) - val jsonSerialFormat = createModuleBasedOnConfigRoot(json) + val jsonSerialFormat = createModuleBasedOnConfigRoot(json, moduleBuilder, decoder.context) val resultJson = JsonObject( json.keys.associateWith { JsonPrimitive(it) } ) diff --git a/src/commonMain/kotlin/com/insanusmokrassar/sdi/utils/DependencyResolver.kt b/src/commonMain/kotlin/com/insanusmokrassar/sdi/utils/DependencyResolver.kt index 042a909..8c0eaa0 100644 --- a/src/commonMain/kotlin/com/insanusmokrassar/sdi/utils/DependencyResolver.kt +++ b/src/commonMain/kotlin/com/insanusmokrassar/sdi/utils/DependencyResolver.kt @@ -2,11 +2,11 @@ package com.insanusmokrassar.sdi.utils import kotlinx.serialization.* import kotlinx.serialization.json.* -import kotlinx.serialization.modules.SerializerAlreadyRegisteredException import kotlinx.serialization.modules.SerializersModuleBuilder import kotlin.reflect.KClass -@InternalSerializationApi +internal object AlreadyRegisteredException : Exception() + @ImplicitReflectionSerializer internal class DependencyResolver( serialModuleBuilder: SerializersModuleBuilder, @@ -17,19 +17,22 @@ internal class DependencyResolver( private val originalSerializer: KSerializer = try { kClass.serializer() } catch (e: Exception) { - PolymorphicSerializer(kClass) + ContextSerializer(kClass) } private val objectsCache = mutableMapOf() override val descriptor: SerialDescriptor = originalSerializer.descriptor init { serialModuleBuilder.apply { - contextual(kClass, this@DependencyResolver) - polymorphic(kClass, originalSerializer) + try { + contextual(kClass, this@DependencyResolver) + } catch (e: IllegalArgumentException) { + throw AlreadyRegisteredException + } kClass.allSubclasses.forEach { currentKClass -> try { DependencyResolver(serialModuleBuilder, currentKClass, formatterGetter, dependencyGetter) - } catch (e: SerializerAlreadyRegisteredException) { + } catch (e: AlreadyRegisteredException) { // ok } } diff --git a/src/commonMain/kotlin/com/insanusmokrassar/sdi/utils/JsonAdaptations.kt b/src/commonMain/kotlin/com/insanusmokrassar/sdi/utils/JsonAdaptations.kt index 5d54b5b..30653bf 100644 --- a/src/commonMain/kotlin/com/insanusmokrassar/sdi/utils/JsonAdaptations.kt +++ b/src/commonMain/kotlin/com/insanusmokrassar/sdi/utils/JsonAdaptations.kt @@ -3,8 +3,7 @@ package com.insanusmokrassar.sdi.utils import kotlinx.serialization.ImplicitReflectionSerializer import kotlinx.serialization.InternalSerializationApi import kotlinx.serialization.json.* -import kotlinx.serialization.modules.SerializerAlreadyRegisteredException -import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.* private typealias PackageOrOtherDependencyNamePair = Pair @@ -22,9 +21,12 @@ private fun JsonElement.resolvePackageName(currentKey: String, otherDependencies } } -@InternalSerializationApi @ImplicitReflectionSerializer -internal fun createModuleBasedOnConfigRoot(jsonObject: JsonObject): Json { +internal fun createModuleBasedOnConfigRoot( + jsonObject: JsonObject, + moduleBuilder: (SerializersModuleBuilder.() -> Unit)? = null, + baseContext: SerialModule +): Json { lateinit var caches: Map Any> lateinit var jsonStringFormat: Json caches = jsonObject.keys.map { key -> @@ -66,23 +68,28 @@ internal fun createModuleBasedOnConfigRoot(jsonObject: JsonObject): Json { key to packageName }.toMap() - val context = SerializersModule { - keysToPackages.values.forEach { - val kclass = resolveKClassByPackageName(it) + val context = baseContext.overwriteWith( + SerializersModule { + keysToPackages.values.forEach { + val kclass = resolveKClassByPackageName(it) - try { - DependencyResolver( - this, - kclass, - { jsonStringFormat } - ) { - caches.getValue(it).invoke() + try { + DependencyResolver( + this, + kclass, + { jsonStringFormat } + ) { + caches.getValue(it).invoke() + } + } catch (e: AlreadyRegisteredException) { + // here we are thinking that already registered } - } catch (e: SerializerAlreadyRegisteredException) { - // here we are thinking that already registered + } + if (moduleBuilder != null) { + moduleBuilder() } } - } + ) return Json( configuration = JsonConfiguration(useArrayPolymorphism = true), context = context diff --git a/src/commonTest/kotlin/com/insanusmokrassar/sdi/SimpleCustomObjectTest.kt b/src/commonTest/kotlin/com/insanusmokrassar/sdi/SimpleCustomObjectTest.kt index 98fe603..579ba31 100644 --- a/src/commonTest/kotlin/com/insanusmokrassar/sdi/SimpleCustomObjectTest.kt +++ b/src/commonTest/kotlin/com/insanusmokrassar/sdi/SimpleCustomObjectTest.kt @@ -31,8 +31,17 @@ class SimpleCustomObject_CustomController2(@ContextualSerialization val service: println("Inited with name \"${service.names}\"") } } + +@Serializable +class SimpleCustomObject_CustomController3(@ContextualSerialization val service: SimpleCustomObject_ServiceAPI) : SimpleCustomObject_ControllerAPI { + override fun showUp() { + println("Inited with name \"${service.names}\"") + } +} @Serializable class SimpleCustomObject_BusinessService(override val names: List) : SimpleCustomObject_ServiceAPI +@Serializable +class SimpleCustomObject_BusinessService1(override val names: List) : SimpleCustomObject_ServiceAPI @ImplicitReflectionSerializer class SimpleCustomObjectTest { @@ -43,6 +52,7 @@ class SimpleCustomObjectTest { val controllerName = "controller" val customController1Name = "controller1" val customController2Name = "controller2" + val customController3Name = "controller3" val input = """ { "service": [ @@ -75,6 +85,17 @@ class SimpleCustomObjectTest { "names": ${customNames.joinToString(prefix = "[", postfix = "]") { "\"$it\"" }} } } + ], + "$customController3Name": [ + "${SimpleCustomObject_CustomController3::class.qualifiedName}", + { + "service": [ + "${SimpleCustomObject_BusinessService1::class.qualifiedName}", + { + "names": ${customNames.joinToString(prefix = "[", postfix = "]") { "\"$it\"" }} + } + ] + } ] } """.trimIndent() diff --git a/src/jvmMain/kotlin/com/insanusmokrassar/sdi/JVMHelperTools.kt b/src/jvmMain/kotlin/com/insanusmokrassar/sdi/JVMHelperTools.kt index 8f9c0bc..57d7c48 100644 --- a/src/jvmMain/kotlin/com/insanusmokrassar/sdi/JVMHelperTools.kt +++ b/src/jvmMain/kotlin/com/insanusmokrassar/sdi/JVMHelperTools.kt @@ -2,17 +2,30 @@ package com.insanusmokrassar.sdi import kotlinx.serialization.ImplicitReflectionSerializer import kotlinx.serialization.json.Json +import kotlinx.serialization.modules.SerializersModuleBuilder import java.io.File import java.io.InputStream @ImplicitReflectionSerializer -fun Json.loadModule(stream: InputStream) = loadModule(stream.reader().readText()) +fun Json.loadModule( + stream: InputStream, + moduleBuilder: (SerializersModuleBuilder.() -> Unit)? = null +) = loadModule(stream.reader().readText(), moduleBuilder) @ImplicitReflectionSerializer -fun loadModule(stream: InputStream) = nonStrictJson.loadModule(stream.reader().readText()) +fun loadModule( + stream: InputStream, + moduleBuilder: (SerializersModuleBuilder.() -> Unit)? = null +) = nonStrictJson.loadModule(stream, moduleBuilder) @ImplicitReflectionSerializer -fun Json.loadModule(file: File) = loadModule(file.inputStream()) +fun Json.loadModule( + file: File, + moduleBuilder: (SerializersModuleBuilder.() -> Unit)? = null +) = loadModule(file.inputStream(), moduleBuilder) @ImplicitReflectionSerializer -fun loadModule(file: File) = nonStrictJson.loadModule(file.inputStream()) +fun loadModule( + file: File, + moduleBuilder: (SerializersModuleBuilder.() -> Unit)? = null +) = nonStrictJson.loadModule(file, moduleBuilder)