如何通过Retrofit在@Query中传递自定义枚举? [英] How to pass custom enum in @Query via Retrofit?

查看:702
本文介绍了如何通过Retrofit在@Query中传递自定义枚举?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个简单的枚举:
$ 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆