kotlinx.serialization
kotlinx.serialization copied to clipboard
Serializer of kind ENUM cannot be serialized polymorphically with class discriminator
Describe the bug java.lang.IllegalArgumentException: Serializer for ChangeAlarmTypeRequest of kind ENUM cannot be serialized polymorphically with class discriminator
All subclasses of MethodExecuteCode have this problem
To Reproduce
polymorphic(MethodExecuteCode::class) {
subclass(MethodExecuteStateTypeCode::class)
subclass(ChangeAlarmTypeRequest::class)
subclass(IncidentMethodCode::class)
}
interface MethodExecuteCode
@Serializable
enum class MethodExecuteStateTypeCode: MethodExecuteCode {
@SerialName("create") CREATE,
@SerialName("enable") ENABLE,
@SerialName("disable") DISABLE,
@SerialName("delete") DELETE,
}
@Serializable
enum class ChangeAlarmTypeRequest : MethodExecuteCode {
@SerialName("alarm") START,
@SerialName("cancel") STOP,
@SerialName("update") UPDATE
}
@Serializable
enum class IncidentMethodCode: MethodExecuteCode {
@SerialName("accept") ACCEPT,
@SerialName("reject") REJECT,
}
Environment
- Kotlin version: 1.5.0
- Library version: 1.2.1
- Kotlin platforms: JVM Android
- Gradle version: 7.0.0
- Other relevant context: Java 8
As the error indicates, "ENUM cannot be serialized polymorphically with class discriminator".
The reason for this is enums are serialized as a primitive kind by default. E.g., in the case of JSON it simply tries to output the SerialName. There is no JSON object to place the class discriminator in, e.g., {"type": "fully.qualified.Name"} by default.
The good news is kotlinx.serialization is quite flexible and you can set up a custom serializer to work around this. This is why I wrote a PolymorphicEnumSerializer.
A serializer which supports registering [Enum]s as subclasses in polymorphic serialization when class discriminators are used. When class discriminators are used, an enum is not encoded as a structure which the class discriminator can be added to. An exception is thrown when initializing [Json]: " "Serializer for
of kind ENUM cannot be serialized polymorphically with class discriminator." This serializer encodes the enum as a structure with a single valueholding the enum value.Use this serializer to register the enum in the serializers module, e.g.:
subclass( <enum>::class, PolymorphicEnumSerializer( <enum>.serializer() )
@Whathecode Actually, i dont want to have a class discriminator, the default enum serializer behaviour is really what i want
e.g:
@Serializable
class MethodExecute(@SerialName("code") val code: MethodExecuteCode? = null,) with ChangeAlarmTypeRequest.START must be {"code":"alarm"}
I tried to write my custom serializer based on your code by replacing buildClassSerialDescriptor with PrimitiveSerialDescriptor but I get similar primary error
Actually, i dont want to have a class discriminator, the default enum serializer behaviour is really what i want
The Json encoder is configured to use a class discriminator by default for polymorphic serialization. The fact that you are getting that error message means that the Json encoder is configured as such (the alternative being array polymorphism in which case type is specified as the first element of a two-element array).
A class discriminator needs to be added in order to be able to deserialize polymorphic type (MethodExecuteCode is polymorphic), unless you are using the Json encoder and you set up a custom JsonContentPolymorphicSerializer.
Think of it this way: how does the deserializer know to initialize ChangeAlarmTypeRequest based on {"code":"alarm"}? E.g., what if there is also an alarm in IncidentMethodCode? Should it deserialize as IncidentMethodCode.ALARM or as ChangeAlarmTypeRequest.START?
The JsonContentPolymorphicSerializer allows you to based on the content (e.g. "alarm") select the serializer (e.g. IncidentMethodCode.serializer()). But, you'd only be able to implement that logic as a two-way conversion if you can uniformly determine based on content which serializer to use. Once you can do that, the type information is irrelevant. But, by default the type information is added because that's a big assumption and requires custom serialization logic based on business logic. Not adding the type also forms a maintenance risk. Now anybody adding additional MethodExecuteCode instances needs to be aware about this and update your custom serializer. You could also ask yourself, what is the use case for this base class either way?
@Whathecode I don't need to deserialize this json. It will go to the server