如何通过Retrofit在@Query中传递自定义枚举? [英] How to pass custom enum in @Query via Retrofit?
问题描述
我有一个简单的枚举:
$ b
public enum Season {
@SerializedName (0)
AUTUMN,
@SerializedName(1)
SPRING;
}
从一些版本开始,GSON就能够解析这些枚举。为了确保,我做到了这一点:
$ b
final String s = gson.toJson(Season.AUTUMN );
它按我的预期工作。输出是0
。所以,我试着在我的Retrofit服务中使用它:
$ b
@GET(index.php? page [api] = test)
可观察< List< Month>> getMonths(@Query(season_lookup)季节季节);
/*...some以后的文件... * /
service.getMonths(Season.AUTUMN);
并且还添加了日志记录以确保其结果:
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient httpClient = new OkHttpClient.Builder()
.addInterceptor(httpLoggingInterceptor)
.build();
但它失败了。 @Query
完全忽略 @SerializedName
并使用 .toString()
相反,所以日志显示我 ... / index.php?page [api] = test& season_lookup = AUTUMN
。
我跟踪了Retrofit的源代码,发现了带有行的文件 RequestFactoryParser
:
Converter<?,String> converter =
retrofit.stringConverter(parameterType,parameterAnnotations);
action = new RequestAction.Query<>(name,converter,encoded);
似乎并不关心枚举。在这些行之前,它测试了 rawParameterType.isArray()
是一个数组还是 Iterable.class.isAssignableFrom()
并且没有更多。
改造实例创建:
retrofit = new Retrofit.Builder()
.baseUrl(ApiConstants.API_ENDPOINT)
.client(httpClient)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
gson
是 GsonBuilder()。创建()
。我在源代码中查看过,在枚举中有预定义的 ENUM_TypeAdapters.ENUM_FACTORY
,所以我保留原样。
< hr>
问题是我该怎么做,以防止使用 toString()
在我的枚举上并使用 @SerializedName
?为了其他目的,我使用 toString()
正如@DawidSzydło所提到的,我误解了Retrofit中的 Gson
用法。它仅用于响应/请求解码/编码,但不适用于 @ Query / @ Url / @ Path e.t.c
。对于他们来说,Retrofit使用 Converter.Factory
将任何类型转换为 String
。
这里是自动使用 @SerializedName
作为任何 Enum
在将其传递给Retrofit服务时的值的代码。
转换器:
public class EnumRetrofitConverterFactory extends Converter.Factory {
@Override
公共转换器<?,String> stringConverter(Type type,Annotation [] annotations,Retrofit retrofit){
Converter<?,String> converter = null; (类型instanceof Class&&((Class<>)type).isEnum()){
converter = value - >
if EnumUtils.GetSerializedNameValue((Enum)value);
}
返回转换器;
$ / code $ / pre
$ b $ EnumUtils:
public class EnumUtils {
@Nullable
static public< E extends Enum< E>> String GetSerializedNameValue(E e){
String value = null;
尝试{
value = e.getClass()。getField(e.name())。getAnnotation(SerializedName.class).value();
} catch(NoSuchFieldException异常){
exception.printStackTrace();
}
返回值;
改造创建:
retrofit = new Retrofit.Builder()
.baseUrl(ApiConstants.API_ENDPOINT)
.client(httpClient)
.addConverterFactory (GsonConverterFactory.create(gson))
.addConverterFactory(new EnumRetrofitConverterFactory())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
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.
解决方案 As @DawidSzydło mentioned, I misunderstood Gson
usage in the Retrofit. It is used only for response/request decoding/encoding, but not for @Query/@Url/@Path e.t.c
. For them, Retrofit uses Converter.Factory
to convert any type to String
.
Here is code for auto using @SerializedName
as value of any Enum
when passing it to Retrofit services.
Converter:
public class EnumRetrofitConverterFactory extends Converter.Factory {
@Override
public Converter<?, String> stringConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
Converter<?, String> converter = null;
if (type instanceof Class && ((Class<?>)type).isEnum()) {
converter = value -> EnumUtils.GetSerializedNameValue((Enum) value);
}
return converter;
}
}
EnumUtils:
public class EnumUtils {
@Nullable
static public <E extends Enum<E>> String GetSerializedNameValue(E e) {
String value = null;
try {
value = e.getClass().getField(e.name()).getAnnotation(SerializedName.class).value();
} catch (NoSuchFieldException exception) {
exception.printStackTrace();
}
return value;
}
}
Retrofit creation:
retrofit = new Retrofit.Builder()
.baseUrl(ApiConstants.API_ENDPOINT)
.client(httpClient)
.addConverterFactory(GsonConverterFactory.create(gson))
.addConverterFactory(new EnumRetrofitConverterFactory())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
这篇关于如何通过Retrofit在@Query中传递自定义枚举?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!