package dev.inmo.postssystem.features.roles.common import kotlinx.serialization.* import kotlinx.serialization.builtins.serializer import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.* import kotlinx.serialization.json.* @Polymorphic interface Role { companion object { fun serializer(): KSerializer = RoleSerializer } } @Serializable data class UnknownRole(val originalJson: JsonElement) : Role object RoleSerializer : KSerializer { private val userRoleFormat = Json { ignoreUnknownKeys = true } private const val keyField = "key" private const val valueField = "value" private val serializers = mutableMapOf>() override val descriptor: SerialDescriptor = String.serializer().descriptor @OptIn(InternalSerializationApi::class) override fun deserialize(decoder: Decoder): Role { return if (decoder is JsonDecoder) { val originalJson = decoder.decodeJsonElement().jsonObject val type = originalJson[keyField]?.jsonPrimitive ?.content return if (type == null || !serializers.containsKey(type)) { UnknownRole(originalJson) } else { userRoleFormat.decodeFromJsonElement( serializers.getValue(type), originalJson[valueField] ?: buildJsonObject { } ) } } else { val encoded = decoder.decodeString() userRoleFormat.decodeFromString(this, encoded) } } @InternalSerializationApi private fun T.toJson(): JsonElement { return userRoleFormat.encodeToJsonElement(this::class.serializer() as KSerializer, this) } @OptIn(InternalSerializationApi::class) override fun serialize(encoder: Encoder, value: Role) { if (encoder is JsonEncoder) { if (value is UnknownRole) { encoder.encodeJsonElement(value.originalJson) } else { val valueSerializer = value::class.serializer() val type = serializers.keys.first { serializers[it] == valueSerializer } encoder.encodeJsonElement( buildJsonObject { put(keyField, type) put(valueField, value.toJson()) } ) } } else { encoder.encodeString( userRoleFormat.encodeToString(this, value) ) } } fun includeSerializer( type: String, kSerializer: KSerializer ) { serializers[type] = kSerializer } fun excludeSerializer(type: String) { serializers.remove(type) } }