Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

polymorphicDefault is never invoked #1585

Closed
cy6erGn0m opened this issue Jul 7, 2021 · 4 comments
Closed

polymorphicDefault is never invoked #1585

cy6erGn0m opened this issue Jul 7, 2021 · 4 comments

Comments

@cy6erGn0m
Copy link

Describe the bug

A serializer lookup lambda registered via polymorphicDefault in a module is never used.

To Reproduce

@Serializable
data class X(@Polymorphic val any: Any)

private val testModule = SerializersModule {
  polymorphic(Any::class, String::class, String.serializer())
  polymorphic(Any::class, Int::class, Int.serializer())
  polymorphic(Any::class, Long::class, Long.serializer())
//  polymorphic(Any::class, Float::class, Float.serializer()) // uncomment to make it work

  polymorphicDefault(Any::class) { className ->
    when (className) {
      "kotlin.Float" -> Float.serializer() // never invoked
      else -> null
    }
  }
}

fun main() {
  val json = Json {
    serializersModule = testModule
    useArrayPolymorphism = true // the only way to have polymorphic working in json
  }

  println(json.encodeToString(X(1.0f)))
}

Expected behavior
I expect polymorphicDefault to be invoked in this case with serial name kotlin.Float.

So it should print the following instead of a crash:

{"any":["kotlin.Float",1.0]}

Environment

  • Kotlin version: 1.5.10
  • Library version: 1.2.1
  • Kotlin platforms: JVM
@pdvrieze
Copy link
Contributor

pdvrieze commented Jul 8, 2021

polymorphicDefault is only supported for decoding, not for encoding. See:

internal typealias PolymorphicProvider<Base> = (className: String?) -> DeserializationStrategy<out Base>?

and
override fun <T : Any> getPolymorphic(baseClass: KClass<in T>, value: T): SerializationStrategy<T>? {
if (!value.isInstanceOf(baseClass)) return null
return polyBase2Serializers[baseClass]?.get(value::class) as? SerializationStrategy<T>
}
override fun <T : Any> getPolymorphic(baseClass: KClass<in T>, serializedClassName: String?): DeserializationStrategy<out T>? {
// Registered
val registered = polyBase2NamedSerializers[baseClass]?.get(serializedClassName) as? KSerializer<out T>
if (registered != null) return registered
// Default
return (polyBase2DefaultProvider[baseClass] as? PolymorphicProvider<T>)?.invoke(serializedClassName)
}

It is mainly designed to support handling unknown inputs from a network, not to circumvent the module system.

@sandwwraith
Copy link
Member

Yep, it's only invoked on deserialization, but it seems there are several use-cases where it may be useful on serialization too (see similar #1317)

@cy6erGn0m
Copy link
Author

This is inconsistent and assymmetric. Should work the same in both directions

@sandwwraith
Copy link
Member

Duplicate of #1317 — fixed by #1686

@sandwwraith sandwwraith closed this as not planned Won't fix, can't repro, duplicate, stale Oct 24, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants