36

How can I parse JSON to model with enum?

Here is my enum class:

enum class VehicleEnumEntity(val value: String) {
   CAR("vehicle"),
   MOTORCYCLE("motorcycle"),
   VAN("van"),
   MOTORHOME("motorhome"),
   OTHER("other")
}

and I need to parse type into an enum

"vehicle": { "data": { "type": "vehicle", "id": "F9dubDYLYN" } }

EDIT

I have tried standard way, just pass my enum to POJO and it always null

LordRaydenMK
  • 13,074
  • 5
  • 50
  • 56
Pasha Shkaran
  • 1,433
  • 2
  • 23
  • 41

3 Answers3

92
enum class VehicleEnumEntity(val value: String) {
   @SerializedName("vehicle")
   CAR("vehicle"),

   @SerializedName("motorcycle")
   MOTORCYCLE("motorcycle"),

   @SerializedName("van")
   VAN("van"),

   @SerializedName("motorhome")
   MOTORHOME("motorhome"),

   @SerializedName("other")
   OTHER("other")
}

Source

LordRaydenMK
  • 13,074
  • 5
  • 50
  • 56
16

Another option: use a custom (de)serializer that uses the value of the enum, instead of the name (default). This means you don't need to annotate every enum value, but instead you can annotate the enum class (or add the adapter to GsonBuilder).

interface HasValue {
    val value: String
}

@JsonAdapter(EnumByValueAdapter::class)
enum class VehicleEnumEntity(override val value: String): HasValue {
   CAR("vehicle"),
   MOTORCYCLE("motorcycle"),
   VAN("van"),
   MOTORHOME("motorhome"),
   OTHER("other")
}

class EnumByValueAdapter<T> : JsonDeserializer<T>, JsonSerializer<T>
    where T : Enum<T>, T : HasValue {
    private var values: Map<String, T>? = null

    override fun deserialize(
        json: JsonElement, type: Type, context: JsonDeserializationContext
    ): T? =
        (values ?: @Suppress("UNCHECKED_CAST") (type as Class<T>).enumConstants
            .associateBy { it.value }.also { values = it })[json.asString]

    override fun serialize(
        src: T, type: Type, context: JsonSerializationContext
    ): JsonElement = JsonPrimitive(src.value)
}

The same adapter class is reusable on other enum classes.

ephemient
  • 198,619
  • 38
  • 280
  • 391
  • This works nicely if the enum type is String. Is there a way to create a generic adapter for any type? The most common enum values are strings and integers I think, but it would be nice to have a generic way to serialize any type. – Sergio Dec 08 '20 at 15:28
3

In case the backend service responses integers for example. You can you change the constructor parameter as Int.

enum class Day(val rawValue: Int) {
    @SerializedName("1")
    SUNDAY(1),
    @SerializedName("2")
    MONDAY(2),
    @SerializedName("3")
    TUESDAY(3),
    @SerializedName("4")
    WEDNESDAY(4),
    @SerializedName("5")
    THURSDAY(5),
    @SerializedName("6")
    FRIDAY(6),
    @SerializedName("7")
    SATURDAY(7),
    @SerializedName("-1")
    UNSUPPORTED(-1);

    companion object {
        fun from(findValue: Int): Day = values().firstOrNull { it.rawValue == findValue } ?: UNSUPPORTED
    }
}

Nitpick: The from function helps you to find an enum value easily by a given Int.