Serialize Query-Parameter in Retrofit

前端 未结 4 1964
挽巷
挽巷 2021-02-14 12:17

Imagine the following request:

@POST(\"/recipes/create\")
void createRecipe(@Query(\"recipe\") Recipe recipe, Callback callback);

相关标签:
4条回答
  • 2021-02-14 12:36

    As @Rolf mentioned, there is a way to set customConverter.Factory Example:

        public class QueryConverterFactory extends Converter.Factory {
        public static QueryConverterFactory create() {
            return new QueryConverterFactory();
        }
    
        private QueryConverterFactory() {
        }
    
        @Nullable
        @Override
        public Converter<?, String> stringConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
            if (type == Date.class) {
                return DateQueryConverter.INSTANCE;
            }
            return null;
        }
    
        private static final class DateQueryConverter implements Converter<Date, String> {
            static final DateQueryConverter INSTANCE = new DateQueryConverter();
    
            private static final ThreadLocal<DateFormat> DF = new ThreadLocal<DateFormat>() {
                @Override
                public DateFormat initialValue() {
                    return new SimpleDateFormat("yyyy-MM-dd");
                }
            };
    
            @Override
            public String convert(Date date) {
                return DF.get().format(date);
            }
        }
    }
    

    You can add converters for your own types.

    0 讨论(0)
  • 2021-02-14 12:37

    I don't think it supports this right now in some nice way. Check this answer by one of the authors: https://github.com/square/retrofit/issues/291

    The suggested method from that answer is to create a custom type that overrides the toString() method, because Retrofit internally uses String.valueOf(value) to convert query or path parameters to strings.

    So, you could have something like this:

    class Recipe {
      public int id;
      public String title;
    
      @Override
      public String toString() {
        // You can use a GSON serialization here if you want
        // This is not a robust implementation
        return Integer.toString(id) + "-" + title;
      }
    }
    
    0 讨论(0)
  • 2021-02-14 12:43

    You can use retrofit.RestAdapter.Builder().setConverter(...) to pass a custom json converter.

    0 讨论(0)
  • 2021-02-14 12:48

    This is now possible when registering a custom Converter.Factory that overrides the stringConverter method, which is called when resolving parameters. The Github issue that @William referred to 2 years ago doesn't seem to be updated since support was added.

    Method Javadoc:

    Returns a Converter for converting type to a String, or null if type cannot be handled by this factory. This is used to create converters for types specified by @Field, @FieldMap values, @Header, @HeaderMap, @Path, @Query, and @QueryMap values.

    The example below delegates to Gson, but in the same way any type of conversion can be applied to the parameters.

    Example: GsonStringConverterFactory

    class GsonStringConverterFactory
        extends Converter.Factory {
    
        private final transient Gson gson;
    
        GsonStringConverterFactory(final Gson gson) {
    
            this.gson = gson;
        }
    
        @Override
        public Converter<?, String> stringConverter(final Type type, final Annotation[] annotations, final Retrofit retrofit) {
    
            final TypeAdapter typeAdapter;
            typeAdapter = gson.getAdapter(TypeToken.get(type));
    
            return new StringConverter<>(typeAdapter);
        }
    
        private static class StringConverter<T>
                implements Converter<T, String> {
    
            private final TypeAdapter<T> typeAdapter;
    
            private StringConverter(final TypeAdapter<T> typeAdapter) {
    
                this.typeAdapter = typeAdapter;
            }
    
            @Override
            public String convert(final T value)
                    throws IOException {
    
                /* This works in our case because parameters in this REST api are always some kind of scalar 
                 * and the toJson method converts these to simple json types. */
                final String jsonValue;
                jsonValue = typeAdapter.toJson(value));
    
                if (jsonValue.startsWith("\"") && jsonValue.endsWith("\"") {
                    /* Strip enclosing quotes for json String types */
                    return jsonValue.substring(1, jsonValue.length() - 1);
                } else {
                    return jsonValue;
                }
            }
        }
    }
    

    Registering the converter:

    To register the custom converter, your Retrofit builder could look something like this:

        new Retrofit.Builder().baseUrl(BASE_URL)
                              .addConverterFactory(GsonConverterFactory.create(gson))
                              .addConverterFactory(new GsonStringConverterFactory(gson))
                              .build();
    
    0 讨论(0)
提交回复
热议问题