2016-03-04 6 views
6

私は、単純な列挙型があります。Retrofit経由で@Queryのカスタム列挙型を渡すには?

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

いくつかのバージョンを開始し、GSONは、このような列挙型を解析することが可能となりました。確認するために、私はこれをしました:

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

私は期待どおりに動作します。出力は"0"です。

@GET("index.php?page[api]=test") 
Observable<List<Month>> getMonths(@Query("season_lookup") Season season); 
/*...some files later...*/ 
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と表示されます。

私はレトロフィットソースをトレースし線でファイル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(); 

gsonGsonBuilder().create()です。私はソースを覗いて、enumのためにあらかじめ定義されたENUM_TypeAdapters.ENUM_FACTORYがあるので、そのまま残しておきます。


質問は私の列挙型にtoString()を使用して防止し@SerializedNameを使用するように、私は何ができるのでしょうか?私は他の目的のためにtoString()を使用します。

+1

を持っていますか? https://github.com/square/retrofit/tree/master/retrofit-converters –

+1

Retrofitは

@Body
をシリアライズしている間にGsonを使用していますが、
@Body
では正しい方法でシリアライズされているため、
@Query
ではありません。 –

+0

@DawidSzydłoですが、 'Enum 'のコンバーターのようなものを書く方法はありますか? '@ SerializedName'のためにリフレクションを使用したいと思います。 – Nexen

答えて

6

@DawidSzydłoが言及したように、私は改造でGsonの使い方を誤解しました。応答/要求のデコード/エンコーディングにのみ使用されますが、@Query/@Url/@Path e.t.cでは使用されません。彼らのために、Retrofitはを使用して任意のタイプをStringに変換します。 Retrofitサービスに渡すときの値としてを値Enumとして自動使用するコードです。

コンバータ:

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 = new Retrofit.Builder() 
     .baseUrl(ApiConstants.API_ENDPOINT) 
     .client(httpClient) 
     .addConverterFactory(GsonConverterFactory.create(gson)) 
     .addConverterFactory(new EnumRetrofitConverterFactory()) 
     .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) 
     .build(); 
+2

はうまく見えますが、他のリクエストで名前付きの注釈を直列化せずにenumを使いたい場合は失敗します。基本的にはすぐに修正されます:ヘルパークラスのcatchでは、注釈が使用されていない場合に値の値をenum値に設定する必要があります。 –

関連する問題