2

I have generated some models using the Open API tool on one of our backend APIs with Swagger. I got the following as the enum definition:

@Serializable
enum class ClientBackgroundState(val value: kotlin.Int) {

    @SerialName(value = "0")
    NONE(0),

    @SerialName(value = "1")
    FOREGROUND(1),

    @SerialName(value = "2")
    BACKGROUND(2);
}

When I use Kotlin Serializer, it serializes the above type into a String like "FOREGROUND" and the Backend API explodes because it wants an Integer.

Is there a way to configure the serializer to convert this enum to an Integer?

FailedUnitTest
  • 1,637
  • 3
  • 20
  • 43
  • 2
    See https://github.com/Kotlin/kotlinx.serialization/issues/31#issuecomment-522943969. – Alexey Romanov May 30 '22 at 12:50
  • Why is the backend 'exploding'? Isn't it using Kotlinx Serialization to decode your enum? – aSemy May 30 '22 at 18:09
  • 1
    Backend, is another team altogether, running .NET @aSemy – FailedUnitTest May 31 '22 at 04:26
  • 1
    @FailedUnitTest Ah okay, thanks, I understand. Kotlinx Serialization doesn't provide this out-of-the-box, you'd have to write your own serializer and tell OpenAPI to use it. But in OpenAPI generator it looks like there's an option for it - see the [test here](https://github.com/OpenAPITools/openapi-generator/blob/master/samples/openapi3/client/petstore/kotlin-multiplatform/src/commonMain/kotlin/org/openapitools/client/models/EnumTest.kt). Or you can ask the .NET team to accept strings - that might be the easiest! – aSemy May 31 '22 at 09:06
  • Can you share the relevant OpenAPI spec for `ClientBackgroundState`? Is `type` set to be `integer`, like [this example](https://github.com/OpenAPITools/openapi-generator/blob/12cdacabbffa60fb8ebbac3fcc53f19bc41c45fa/samples/client/petstore/java/webclient/api/openapi.yaml#L1783-L1789)? What version of the generator are you using? – aSemy May 31 '22 at 09:17
  • Are you certain it serializes this as "FOREGROUND" and not as a _string_ "1"? That is [what the `@SerialName` annotation does](https://github.com/Kotlin/kotlinx.serialization/issues/31#issuecomment-559571106). Of course that would still fail if the backend expects an integer. Just trying to understand whether you pinpointed the exact problem correctly; this is suspicious. – Steven Jeuris Jul 17 '22 at 15:13

1 Answers1

3

I have this solution. Create a "simple" base class:

open class EnumAsIntSerializer<T:Enum<*>>(
    serialName: String,
    val serialize: (v: T) -> Int,
    val deserialize: (v: Int) -> T
) : KSerializer<T> {
    override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor(serialName, PrimitiveKind.INT)

    override fun serialize(encoder: Encoder, value: T) {
        encoder.encodeInt(serialize(value))
    }

    override fun deserialize(decoder: Decoder): T {
        val v = decoder.decodeInt()
        return deserialize(v)
    }
}

And use it this way per an enum type:

private class PartOfSpeechSerializer: EnumAsIntSerializer<PartOfSpeech>(
    "PartOfSpeech",
    { it.value },
    { v -> PartOfSpeech.values().first { it.value == v } }
)

@Serializable(with = PartOfSpeechSerializer::class)
enum class PartOfSpeech(val value: Int) {
    Undefined(0),
    Noun(1),
    Verb(2),
    Adjective(3),
    Adverb(4),
    Pronoun(5),
    Preposition(6),
    Conjunction(7),
    Interjection(8)
}
Alexey
  • 440
  • 3
  • 12