2016-03-04 14 views
6

Ho una semplice enum:Come passare enum personalizzato in @Query tramite Retrofit?

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

partire qualche versione, GSON divenne in grado di analizzare tali enumerazioni. Per essere sicuro, ho fatto questo:

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

Funziona come mi aspettavo. L'output è "0". Così, ho provato usarlo nei miei servizi Retrofit:

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

ed anche ha aggiunto la registrazione per essere davvero certi circa il suo risultato:

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

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

Ma è fallito. @Query totalmente ignorato @SerializedName e utilizzato invece .toString(), quindi il registro mi ha mostrato .../index.php?page[api]=test&season_lookup=AUTUMN.

Ho tracciato fonti Retrofit e hanno trovato il file RequestFactoryParser con le linee:

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

Sembra, come se non si cura affatto di enumerazioni. Prima di queste righe, ha testato rawParameterType.isArray() come array o Iterable.class.isAssignableFrom() e nient'altro.

creazione dell'istanza Retrofit è:

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

gson è GsonBuilder().create(). Ho dato una sbirciatina alle fonti, c'è un numero predefinito di ENUM_TypeAdapters.ENUM_FACTORY per le enumerazioni, quindi lo lascio com'è.


La domanda è: che cosa posso fare, per evitare che utilizzandotoString()sulle enumerazioni e utilizzare@SerializedName? Io usotoString()per altri scopi.

+1

Possiedi convertitore appropriato aggiunto alla tua retrofit? https://github.com/square/retrofit/tree/master/retrofit-converters –

+1

Sembra che Retrofit utilizzi Gson durante la serializzazione di

@Body
, ma non di
@Query
, perché con il numero
@Body
è serializzato correttamente. –

+0

@ DawidSzydło, ma c'è un modo per scrivere qualcosa come il convertitore per 'Enum ' solo? Mi piacerebbe usare il riflesso in questo per '@ SerializedName' – Nexen

risposta

6

Come @ DawidSzydło ha menzionato, ho frainteso l'utilizzo di Gson nel Retrofit. Viene utilizzato solo per la risposta/richiesta di decodifica/codifica, ma non per @Query/@Url/@Path e.t.c. Per loro, Retrofit utilizza Converter.Factory per convertire qualsiasi tipo in String. Questo è il codice per l'utilizzo automatico di @SerializedName come valore di qualsiasi Enum quando lo si passa ai servizi Retrofit.

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; 
    } 
} 

creazione Retrofit:

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

Sembra buono, ma fallirà se si desidera utilizzare enum senza l'annotazione del nome serializzato in altre richieste. Fondamentalmente ci sarà una soluzione rapida per questo: in catch nella classe helper si dovrebbe impostare il valore value sul valore enum nel caso in cui non ci sia annotazione usata. –