I have enums like this:
enum class NewTaskState(val value: Int) {
@SerializedName("0")
STATE_INSTALLED(0),
@SerializedName("1")
STATE_LAUNCHED(1)
}
API call with retrofit:
@POST("tasks/android/{id}/state/update/")
@FormUrlEncoded
fun updateAppTaskState(
@Path("id") id: Long,
@Query("device_pk") deviceId: Long,
@Field("new_state") newState: NewTaskState
): Single<Response<Any>>
And GSON configuration:
return GsonBuilder()
.registerTypeAdapter(Date::class.java, DateDeserializer())
.enableComplexMapKeySerialization()
.create()
Gson version is 2.8.5
.
When I use this call, I see in logs:
I/OkHttp: --> POST <removed>/tasks/android/1/state/update/?device_pk=8
I/OkHttp: Content-Type: application/x-www-form-urlencoded
I/OkHttp: Content-Length: 25
I/OkHttp: Authorization: Token <removed>
I/OkHttp: new_state=STATE_INSTALLED
So it ignores @SerializedName
value on serialization, but works fine on deserialization.
What's wrong?
This happens because Gson
does not participate in url form-encoding.
From @Field
's javadoc:
Values are converted to strings using
Retrofit.stringConverter(Type, Annotation[])
(orObject.toString()
, if no matching string converter is installed) and then form URL encoded.null
values are ignored. Passing aList
or array will result in a field pair for each non-null
item.
And GsonConverterFactory
does not override Converter.Factory#stringConverter
so it is not ignoring anything. It's merely not kicking in
As a workaround you can do something similar:
Converter
:
object EnumAsOrdinalToStringConverter : Converter<Enum<*>, String> {
override fun convert(value: Enum<*>): String =
value.ordinal.toString()
}
Factory
:
class EnumAsOrdinalToStringConverterFactory : Converter.Factory() {
override fun stringConverter(
type: Type,
annotations: Array<Annotation>,
retrofit: Retrofit
): Converter<*, String>? = if (type is Class<*> && type.isEnum) {
EnumAsOrdinalToStringConverter
} else {
null
}
}
Building Retrofit
:
addConverterFactory(EnumAsOrdinalToStringConverterFactory())