How to pass custom enum in @Query via Retrofit?

Viewed 10166

I have a simple enum:

public enum Season {
    @SerializedName("0")
    AUTUMN,
    @SerializedName("1")
    SPRING;
}

Starting some version, GSON became able to parse such enums. To make sure, I did this:

final String s = gson.toJson(Season.AUTUMN);

It works as I expected. Output is "0". So, I tried use it in my Retrofit services:

@GET("index.php?page[api]=test")
Observable<List<Month>> getMonths(@Query("season_lookup") Season season);
/*...some files later...*/
service.getMonths(Season.AUTUMN);

And also added logging to be really certain about its result:

HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

OkHttpClient httpClient = new OkHttpClient.Builder()
        .addInterceptor(httpLoggingInterceptor)
        .build();

But it failed. @Query totally ignored @SerializedName and used .toString() instead, so the log shown me .../index.php?page[api]=test&season_lookup=AUTUMN.

I traced Retrofit sources and found file RequestFactoryParser with lines :

Converter<?, String> converter = 
    retrofit.stringConverter(parameterType, parameterAnnotations);
action = new RequestAction.Query<>(name, converter, encoded);

It seems, like it doesn't care at all about enums. Before these lines, it tested rawParameterType.isArray() to be an array or Iterable.class.isAssignableFrom() and nothing more.

Retrofit instance creation is:

retrofit = new Retrofit.Builder()
                .baseUrl(ApiConstants.API_ENDPOINT)
                .client(httpClient)
                .addConverterFactory(GsonConverterFactory.create(gson))
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .build();

gson is GsonBuilder().create(). I peeked at sources, there is predefined ENUM_TypeAdapters.ENUM_FACTORY in it for enums, so I leave it as it is.


The question is what can I do, to prevent using toString() on my enums and use @SerializedName? I use toString() for other purposes.

2 Answers

If you don't need it for all enums, I guess you can also skip the Converter.Factory setup and just add a toString method:

enum class Season {
    @SerializedName("0")
    AUTUMN,
    @SerializedName("1")
    SPRING;

    override fun toString(): String {
        return javaClass
                .getField(name)
                .getAnnotation(SerializedName::class.java)
                .value
    }
}

Or, if you need it for more than one:

val <E : Enum<E>> Enum<E>.serializedName: String
    get() = javaClass
            .getField(name)
            .getAnnotation(SerializedName::class.java)
            .value

enum class Season {
    @SerializedName("0")
    AUTUMN,
    @SerializedName("1")
    SPRING;

    override fun toString() = serializedName
}
Related