diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/AppUtils.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/AppUtils.kt index 6233fd82018..a002deb3155 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/AppUtils.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/AppUtils.kt @@ -9,6 +9,10 @@ import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.InternalSerializationApi import kotlinx.serialization.KSerializer import kotlinx.serialization.SerializationException +import kotlinx.serialization.builtins.ListSerializer +import kotlinx.serialization.builtins.MapSerializer +import kotlinx.serialization.builtins.SetSerializer +import kotlinx.serialization.builtins.serializer import kotlinx.serialization.serializer import kotlinx.serialization.serializerOrNull import kotlin.reflect.KClass @@ -28,19 +32,56 @@ object AppUtils { // registered manually in serializersModule, we need both to support all cases val serializer = this::class.serializerOrNull() ?: json.serializersModule.getContextual(this::class) - return if (serializer != null) { + if (serializer != null) { try { @Suppress("UNCHECKED_CAST") - json.encodeToString(serializer as KSerializer, this) + return json.encodeToString(serializer as KSerializer, this) } catch (e: SerializationException) { logError(e) - mapper.writeValueAsString(this) + return mapper.writeValueAsString(this) + } + } + // Handle generic collection/map types where type params are erased at runtime + // and no serializer can be found via reflection alone + return try { + @Suppress("UNCHECKED_CAST") + when (this) { + is Array<*> -> json.encodeToString(ListSerializer(elementSerializer()), this.toList()) + is Set<*> -> json.encodeToString(SetSerializer(elementSerializer()), this) + is List<*> -> json.encodeToString(ListSerializer(elementSerializer()), this) + is Collection<*> -> json.encodeToString(ListSerializer(elementSerializer()), this.toList()) + is Map<*, *> -> json.encodeToString(MapSerializer(String.serializer(), valueSerializer()), this as Map) + else -> mapper.writeValueAsString(this) } - } else { + } catch (e: SerializationException) { + logError(e) mapper.writeValueAsString(this) } } + private fun elementSerializerForClass(kClass: KClass<*>): KSerializer { + val serializer = kClass.serializerOrNull() + ?: json.serializersModule.getContextual(kClass) + ?: throw SerializationException("No serializer found for element type ${kClass.simpleName}") + @Suppress("UNCHECKED_CAST") + return serializer as KSerializer + } + + private fun Collection<*>.elementSerializer(): KSerializer { + val elementClass = this.firstOrNull()?.let { it::class } ?: String::class + return elementSerializerForClass(elementClass) + } + + private fun Array<*>.elementSerializer(): KSerializer { + val elementClass = this.firstOrNull()?.let { it::class } ?: String::class + return elementSerializerForClass(elementClass) + } + + private fun Map<*, *>.valueSerializer(): KSerializer { + val elementClass = this.values.firstOrNull()?.let { it::class } ?: String::class + return elementSerializerForClass(elementClass) + } + @InternalAPI fun parseJson(value: String, kClass: KClass): T { val serializer = kClass.serializerOrNull() ?: json.serializersModule.getContextual(kClass)