kotlinx.serialization
kotlinx.serialization copied to clipboard
Serializer forClass generates class type serializer for enums
Describe the bug When using the workaround to force generation of a plugin generated serializer for enums, the generated serializer always treats it as a class
To Reproduce
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.Serializer
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
@Serializable
enum class Foo {
HELLO,
WORLD
}
@Serializable(with = Foo2Serializer::class)
enum class Foo2 {
HELLO,
WORLD
}
@Serializer(forClass = Foo2::class)
object Foo2Serializer : KSerializer<Foo2>
fun main() {
println(Json.encodeToString(Foo.HELLO))
println(Json.encodeToString(Foo2.HELLO))
}
Generates:
"HELLO"
{}
Expected behavior
Serializer should act the same as if generated by @Serializable
Environment
- Kotlin version: 1.9.0
- Library version: 1.6
- Kotlin platforms: JVM
- Gradle version: 8.2
External serializer generation is an experimental feature and likely shouldn't be allowed to be used on enums.
That would be very sad to hear unless a new way is added to get access to a plugin generated serializer (#1169)
Right now using the generated serializer gives you support for things like case insensitive enums and @SerialName support. That is a lot of boiler plate to reproduce per enum if you want to wrap the serializer
Right now we use the plugin generated one with a fallback wrapper so that the serialization logic can be forwards compatible:
@Serializable
// Impossible to place this here to reduce copy paste from having to place at the use site: @Serializable(with = FamilySerializer::class)
enum class Family {
@SerialName("foo")
FOO,
UNKNOWN
}
object FamilySerializer : KSerializer<Family> by EnumWithFallbackSerializer(Family.serializer(), Family.UNKNOWN)
class EnumWithFallbackSerializer<T : Enum<T>>(
private val underlyingSerializer: KSerializer<T>,
private val fallbackValue: T
) : KSerializer<T> {
override val descriptor: SerialDescriptor = SerialDescriptor(
"EnumWithFallbackSerializer<${underlyingSerializer.descriptor.serialName}>",
underlyingSerializer.descriptor
)
override fun deserialize(decoder: Decoder): T {
return try {
underlyingSerializer.deserialize(decoder)
} catch (e: SerializationException) {
thisLogger().warn("Error deserializing message ${underlyingSerializer.descriptor.serialName}, falling back to $fallbackValue", e)
fallbackValue
}
}
override fun serialize(encoder: Encoder, value: T) {
underlyingSerializer.serialize(encoder, value)
}
}
So it's another use-case for #1169, I see
Fixed by #1169