0

I am trying to parse a json response that include an enum using the Klaxon library and kotlin. Below is a test. If I have a nullable enum, my parsing fails. Is there a way to do this properly? Is it an issue with the Klaxon libary?

import com.beust.klaxon.Klaxon
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull

enum class MyEnum { FIRST, SECOND, THIRD }

class WithEnum {
    var myVal: MyEnum? = null
}

class EnumTest {
    @Test
    fun `should deserialize null enum`() {
        val parsed = Klaxon().parse<WithEnum>("{ \"myVal\":null}")
        assertNotNull(parsed)
        assertNull(parsed.myVal)
    }

    @Test
    fun `should deserialize proper enum`() {
        val parsed = Klaxon().parse<WithEnum>("{ \"myVal\":\"FIRST\"}")
        assertNotNull(parsed)
        assertEquals(MyEnum.FIRST, parsed.myVal)
    }
}

The response from the above unit test is:

kotlin.TypeCastException: null cannot be cast to non-null type kotlin.String
    at com.beust.klaxon.EnumConverter.fromJson(EnumConverter.kt:23)
    at com.beust.klaxon.EnumConverter.fromJson(EnumConverter.kt:6)
    at com.beust.klaxon.JsonObjectConverter.retrieveKeyValues(JsonObjectConverter.kt:189)
    at com.beust.klaxon.JsonObjectConverter.initIntoUserClass(JsonObjectConverter.kt:67)
    at com.beust.klaxon.JsonObjectConverter.fromJson(JsonObjectConverter.kt:32)
    at com.beust.klaxon.DefaultConverter.fromJsonObject(DefaultConverter.kt:210)
    at com.beust.klaxon.DefaultConverter.fromJson(DefaultConverter.kt:32)
    at com.beust.klaxon.Klaxon.fromJsonObject(Klaxon.kt:296)
    at EnumTest.should deserialize null enum(EnumTest.kt:30)
    ...

There source of the error is https://github.com/cbeust/klaxon/blob/master/klaxon/src/main/kotlin/com/beust/klaxon/EnumConverter.kt#L23, where the null is being cast to a String.

1 Answers1

0

I copied the EnumConverter from the github project and made it nullable, changing line 23 to return a null if the value is a null like so:

       val name = jv.inside as String? ?: return null

I then created an annotation for the fields and added the converter to the parser. The EnumConverter() reference below is my copy of it with the Elvis operator addition above.

...
@Target(AnnotationTarget.FIELD)
annotation class NullableEnum

class WithEnum (
    @NullableEnum
    var myVal: MyEnum? = null
)

class EnumTest {
    @Test
    fun `should deserialize null enum`() {
        val parsed = Klaxon()
            .fieldConverter(NullableEnum::class, EnumConverter())
            .parse<WithEnum>("{ \"myVal\":null}")
        assertNotNull(parsed)
        assertNull(parsed.myVal)
    }
...