Search code examples
jsonkotlinenumsgson

Limit permitted integer values for serialised class property with Kotlin


I'd like to limit the permitted integer values for a class property, in a way that after serialisation to Json (with gson) the value would be represented as a number and not as a string.

class Car(
@Expose val color: Color = Color.RED,
@Expose val seats: Seats = Seats.FIVE   // Using an enum class so only predefined values are accepted.
)

enum class Color{
     @SerializedName("red") RED,
     @SerializedName("blue") BLUE,
}

enum class Seats{
     @SerializedName("4") FOUR,
     @SerializedName("5") FIVE,
}

Actual json output:

{
   "color": "red",
   "seats": "5"   // This is a string. It's not what I want.
}

The json output I need:

{
   "color": "red",
   "seats": 5   // This is a number ✓
}

Solution

  • If you really want to stick to Gson, one way of doing this is to specify the integer as a property of the enum:

    enum class Seats(val value: Int) {
        FOUR(4),
        FIVE(5);
    
        companion object {
            fun of(value: Int) = entries.firstOrNull { it.value == value }
        }
    }
    

    Then use a type adapter or custom serializer that uses this property for serialization, and the Seat.of(...) function for deserialization:

    class SeatSerializer : JsonSerializer<Seats> {
        override fun serialize(src: Seats, typeOfSrc: Type, context: JsonSerializationContext): JsonElement =
            JsonPrimitive(src.value)
    }
    
    class SeatDeserializer : JsonDeserializer<Seats> {
        override fun deserialize(element: JsonElement, type: Type, context: JsonDeserializationContext): Seats =
            Seats.of(element.asInt) ?: error("Cannot deserialize ${element.asInt} as Seats")
    }