在Retrofit中序列化查询参数

时间:2015-02-13 22:47:43

标签: android gson retrofit

想象一下以下请求:

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

我想要json(配方)但不幸的是我的请求只是调用toString()来获取我的配方,这根本不起作用。

我可以覆盖Recipe中的toString但我宁愿有一个通用的解决方案。

我无法使用@Body,因为我需要指定,我要发送的内容(我需要&#34; recipe = json(theRecipe)&#34;。

我也无法更改序列化以添加&#34; recipe =&#34;因为我不负责服务器。

目前我正在使用QueryMap Map放置一个序列化对象。虽然这很有效,但在我看来,它并不是一个很好的解决方案。

我可以以某种方式拦截改装适配器吗?

4 个答案:

答案 0 :(得分:8)

现在可以在注册自定义if _, ok := m["Answer"]; ok { // Do something if set } else { // Do something if not set } 时覆盖Converter.Factory方法,该方法在解析参数时调用。 @William在2年前提到的Github issue似乎没有更新,因为添加了支持。

方法Javadoc:

  

返回用于将类型转换为String的Converter,如果此工厂无法处理类型,则返回null。这用于为@Field,@ FieldMap值,@ Header,@ HeaderMap,@ Path,@ Query和@QueryMap值指定的类型创建转换器。

以下示例委托给Gson,但同样可以将任何类型的转换应用于参数。

示例:GsonStringConverterFactory

stringConverter

注册转换器:

要注册自定义转换器,您的“Retrofit”构建器可能如下所示:

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

答案 1 :(得分:6)

我不认为它现在以一种不错的方式支持这一点。请作者之一检查此答案:https://github.com/square/retrofit/issues/291

该答案建议的方法是创建一个覆盖toString()方法的自定义类型,因为Retrofit在内部使用String.valueOf(value)将查询或路径参数转换为字符串。

所以,你可以这样:

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

答案 2 :(得分:0)

您可以使用retrofit.RestAdapter.Builder()。setConverter(...)来传递自定义json转换器。

答案 3 :(得分:0)

如@Rolf所述,有一种方法可以设置自定义Converter.Factory 示例:

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

您可以为自己的类型添加转换器。