How use Kotlin enum with Retrofit?
Asked Answered
D

4

39

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

Dialogue answered 7/8, 2017 at 18:18 Comment(6)
have you tried somethinig?Overcome
@Overcome yes, I have tried standard way, just pass my enum to POJO and it always nullDialogue
maybe it's worth mentioning it in your questionOvercome
I think the most relevant part here would be which converter are you using... GSON or something else?Baxter
@Baxter yes GSONDialogue
have you tried using @SerializedName annotation? according to this https://mcmap.net/q/409036/-enum-annotations-in-kotlin it should workBaxter
B
97
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

Baxter answered 7/8, 2017 at 18:41 Comment(4)
that's a GSON solution, change to @Json(name="foo") for moshiOpalina
@ravidrinek please ask a new question, and include all relevant details, e.g. json parsing lib etc...Baxter
How can I do it with Int instead of String?Arbitrage
@CarlosLeonardoCamiloVargas you change the String to an Int and make sure backend also returns an IntBaxter
C
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.

Creep answered 8/8, 2017 at 6:42 Comment(1)
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.Robins
A
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.

Arbitrage answered 10/2, 2023 at 16:20 Comment(0)
T
1

If you are using kotlinx.serialization, then you simply need to mark your enum class as @Serializable and use @SerialName with it's values:

@Serializable
enum class VehicleEnumEntity(val value: String) {
   @SerialName("vehicle")
   CAR("vehicle"),

   @SerialName("motorcycle")
   MOTORCYCLE("motorcycle"),

   @SerialName("van")
   VAN("van"),

   @SerialName("motorhome")
   MOTORHOME("motorhome"),

   @SerialName("other")
   OTHER("other")
}
Tocopherol answered 12/12, 2023 at 9:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.