How to pass custom enum in @Query via Retrofit?

前端 未结 1 1166
粉色の甜心
粉色の甜心 2021-01-02 02:44

I have a simple enum:

public enum Season {
    @SerializedName(\"0\")
    AUTUMN,
    @SerializedName(\"1\")
    SPR         


        
相关标签:
1条回答
  • 2021-01-02 03:26

    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();
    

    08.18 update added kotlin analog:

    class MainActivity : AppCompatActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            val httpLoggingInterceptor = HttpLoggingInterceptor()
            httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
    
            val httpClient = OkHttpClient.Builder()
                    .addInterceptor(httpLoggingInterceptor)
                    .build()
    
            val gson = GsonBuilder().create()
    
            val retrofit = Retrofit.Builder()
                    .baseUrl(Api.ENDPOINT)
                    .client(httpClient)
                    .addConverterFactory(GsonConverterFactory.create(gson))
                    .addConverterFactory(EnumConverterFactory())
                    .build()
    
            val service = retrofit.create(Api::class.java)
            service.getMonths(Season.AUTUMN).enqueue(object : Callback<List<String>> {
                override fun onFailure(call: Call<List<String>>?, t: Throwable?) {
                    /* ignore */
                }
    
                override fun onResponse(call: Call<List<String>>?, response: Response<List<String>>?) {
                    /* ignore */
                }
            })
        }
    }
    
    class EnumConverterFactory : Converter.Factory() {
        override fun stringConverter(type: Type?, annotations: Array<out Annotation>?,
                                     retrofit: Retrofit?): Converter<*, String>? {
            if (type is Class<*> && type.isEnum) {
                return Converter<Any?, String> { value -> getSerializedNameValue(value as Enum<*>) }
            }
            return null
        }
    }
    
    fun <E : Enum<*>> getSerializedNameValue(e: E): String {
        try {
            return e.javaClass.getField(e.name).getAnnotation(SerializedName::class.java).value
        } catch (exception: NoSuchFieldException) {
            exception.printStackTrace()
        }
    
        return ""
    }
    
    enum class Season {
        @SerializedName("0")
        AUTUMN,
        @SerializedName("1")
        SPRING
    }
    
    interface Api {
        @GET("index.php?page[api]=test")
        fun getMonths(@Query("season_lookup") season: Season): Call<List<String>>
    
        companion object {
            const val ENDPOINT = "http://127.0.0.1"
        }
    }
    

    In logs you will see this:

    D/OkHttp: --> GET http://127.0.0.1/index.php?page[api]=test&season_lookup=0 
    D/OkHttp: --> END GET 
    D/OkHttp: <-- HTTP FAILED: java.net.ConnectException: Failed to connect to /127.0.0.1:80
    

    Used dependencies are:

    implementation 'com.squareup.retrofit2:retrofit:2.4.0'
    implementation 'com.google.code.gson:gson:2.8.5'
    implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
    
    0 讨论(0)
提交回复
热议问题