2016-03-04 1 views
6

Je simple ENUM:Comment passer enum personnalisé dans @Query via Retrofit?

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

À partir une version, GSON est devenu capable d'analyser ces énumérations. Pour être sûr, je l'ai fait:

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

Cela fonctionne comme prévu. La sortie est "0". Alors, j'ai essayé l'utiliser dans mes services Rénovation:

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

et l'exploitation forestière a également contribué à être vraiment sûr de son résultat:

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

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

Mais il a échoué. @Query totalement ignoré @SerializedName et utilisé .toString() à la place, de sorte que le journal m'a montré .../index.php?page[api]=test&season_lookup=AUTUMN.

I retracée sources et Retrofit trouvé le fichier RequestFactoryParser avec des lignes:

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

Il semble, comme il ne se soucie pas du tout énumérations. Avant ces lignes, il a testé rawParameterType.isArray() pour être un tableau ou Iterable.class.isAssignableFrom() et rien de plus.

création d'instances Rénovation est:

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

gson est GsonBuilder().create(). J'ai jeté un œil aux sources, il y a ENUM_TypeAdapters.ENUM_FACTORY prédéfini dedans pour enums, donc je le laisse tel quel.


La question est ce que je peux faire pour empêcher l'utilisationtoString()sur mes énumérations et utiliser@SerializedName? J'utilisetoString()à d'autres fins.

+1

Est-ce que vous avez convertisseur approprié ajouté à votre Retrofit? https://github.com/square/retrofit/tree/master/retrofit-converters –

+1

Il semble que Retrofit utilise Gson lors de la sérialisation

@Body
, mais pas
@Query
, car avec
@Body
il est sérialisé correctement. –

+0

@ DawidSzydło, mais est-il possible d'écrire quelque chose comme un convertisseur pour 'Enum ' seulement? Je voudrais utiliser la réflexion dans cela pour '@ SerializedName' – Nexen

Répondre

6

Comme @ DawidSzydło mentionné, j'ai mal compris Gson utilisation dans le Retrofit. Il est utilisé uniquement pour la réponse/demande de décodage/codage, mais pas pour @Query/@Url/@Path e.t.c. Pour eux, Retrofit utilise Converter.Factory pour convertir n'importe quel type à String. Voici le code pour auto utilisant @SerializedName comme valeur de tout Enum lors de la transmission aux services 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; 
    } 
} 

création de Rénovation:

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

Cela semble très bien, mais il échouera si vous voulez utiliser enum sans annotation de nom sérialisé dans d'autres demandes. Fondamentalement, il y aura une solution rapide pour cela: dans catch dans votre classe d'aide, vous devez définir la valeur value à la valeur enum si aucune annotation n'est utilisée. –