kotlinx.serialization icon indicating copy to clipboard operation
kotlinx.serialization copied to clipboard

Delegate serialization produces incorrect results on Collection elements with polymorphism

Open abrooksv opened this issue 2 years ago • 4 comments

Describe the bug When using a custom serializer that is polymorphic on the type argument of a list, the serializer produces an array instead of the expected object

To Reproduce

import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json

@Serializable
sealed interface Base

@Serializable
data class GenericError(@SerialName("error_code") val errorCode: Int) : Base

@Serializable
data class Works(val messages: List<Base>)

@Serializable
data class Broken(val messages: List<@Serializable(DelegateSerializer::class) Base>)

object DelegateSerializer : KSerializer<Base> by Base.serializer()

val format = Json { prettyPrint = true }

fun main() {
    /**
     * Produces:
     * {
     *     "messages": [
     *         {
     *             "type": "my.app.GenericError",
     *             "error_code": 404
     *         }
     *     ]
     * }
     */
    println(format.encodeToString(Works(listOf(GenericError(404)))))

    /**
     * Produces:
     * {
     *     "messages": [
     *         ["my.app.GenericError", {
     *                 "error_code": 404
     *             }
     *         ]
     *     ]
     * }
     */
    println(format.encodeToString(Broken(listOf(GenericError(404)))))
}

Expected behavior

Both lines should produce the same output since the delegate serializer should be no-op in this case since everything should be delegated to the generated Base serializer

Environment

  • Kotlin version: 1.9.10
  • Library version: 1.6.0
  • Kotlin platforms: JVM

abrooksv avatar Oct 04 '23 18:10 abrooksv

This bug is connected to the fact that we use is AbstractPolymorphicSerializer check, which the delegate does not satisfy. I do not remember exactly why it is not a PolymorphicKind check, but changing this will have a big impact now.

Related to the #2460

sandwwraith avatar Oct 10 '23 15:10 sandwwraith

Experiencing the same issue

AzimMuradov avatar Feb 07 '24 17:02 AzimMuradov

What's the workaround for this?

EDIT: https://github.com/Kotlin/kotlinx-datetime/blob/master/core/common/src/serializers/DateTimeUnitSerializers.kt#L199

SettingDust avatar Jul 28 '24 13:07 SettingDust

@SettingDust What happens is that for a custom polymorphic serializer (including a delegate) serialization the special treatment by json doesn't work, but rather uses the default behaviour (wrap in a container with type and value properties). As this behaviour is predictable and not incorrect any changes to this would need to be configurable.

The current implementation uses internals of AbstractPolymorphicSerializer rather than a special encoder that only would rely on behaviour (that could be implemented by other serializers). This is very possible, but needs changes in the library (and possibly a configuration flag)

pdvrieze avatar Aug 01 '24 10:08 pdvrieze