1

I a working on a custom JsonSerializer & JsonDeserializer I want to serialize json (ints) to enums and deserialize enums to ints

I have created an interface for this purpose, where the enums have a function called fromValue which should take the deserialized value and return the enum:

interface EnumSerializationInterface {
    val value: Int
    fun fromValue(value: Int): EnumSerializationInterface?
}

example enum:

enum class TestDummyEnumTestEnum(override val value: Int): 
EnumSerializationInterface {
    ENUM1(1),
    ENUM2(2);

    override fun fromValue(value: Int): EnumSerializationInterface? {
        for (enum in TestDummyEnumTestEnum.values()) {
            if (enum.value == value) {
                return enum
            }
        }
        return null
    }
}

My serializer works but i am having trouble deserializing, my serializer & deserializer:

class EnumDeserializer: JsonDeserializer<EnumSerializationInterface> {
    override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): EnumSerializationInterface {
        val value = json.asInt()
        return typeOfT.fromValue(value)   
        /*THIS DOES NOT WORK*/
    }
}

class EnumSerializer: JsonSerializer<EnumSerializationInterface> {
    override fun serialize(src: EnumSerializationInterface?, typeOfSrc: Type?, context: JsonSerializationContext?): JsonElement {
        return context!!.serialize(src!!.value)
    }
}

My issue is that i cannot get the 'fromValue' function from typeOfT.

Does anybody know how to get the actual typeOfT?

Oreex0
  • 324
  • 3
  • 15

1 Answers1

0

Here's my solution. I make use of a companion object to build enum lookups. This makes my coded enums slightly more complex to understand, but there's a little less boilerplate when declaring an enum:

interface CodedEnum {
    val code: Int
}

// It has to take class as parameter, since it is not possible to access T.values()
// This is Java generics limitation: https://stackoverflow.com/questions/2205891/iterate-enum-values-using-java-generics
open class CodedEnumLookup<E>(klass: Class<E>) where E: Enum<E>, E: CodedEnum {
    val lookup = klass.enumConstants.associate { it.code to it }

    init {
        // Make sure no duplicate codes
        check(lookup.size == klass.enumConstants.size)
    }

    fun get(code: Int): E {
        return lookup[code]!!
    }
}

Example usage:

enum class Color(override val code: Int): CodedEnum {

    RED(0),
    GREEN(1),
    BLUE(2);

    companion object: CodedEnumLookup<Color>(Color::class.java)
}

And in Gson, with a little bit of usage of Kotlin's reflection:

object CodedEnumSerializer : JsonSerializer<CodedEnum>, JsonDeserializer<CodedEnum> {
    override fun serialize(src: CodedEnum, typeOfSrc: Type, context: JsonSerializationContext): JsonElement {
        return JsonPrimitive(src.code)
    }

    // Not sure about how performant this solution is
    override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): CodedEnum {
        return ((typeOfT as Class<*>).kotlin.companionObjectInstance as CodedEnumLookup<*>).get(json.asInt) as CodedEnum
    }
}

GsonBuilder().registerTypeHierarchyAdapter(CodedEnum::class.java, CodedEnumSerializer)
Nikola Mihajlović
  • 2,286
  • 2
  • 19
  • 23