Retrofit2:没有为@Field 注释的参数调用序列化程序

Retrofit2: serializer doesn't get called for @Field-annotated parameters

我需要在请求参数之一中发送带有自定义 JSON 对象的 HTTP PUT 请求。这是一个问题:当我将它与 Retrofit2 一起使用时,我的序列化器没有被调用。

我的对象应该是这样的:

{
    "ignore":["item1", "item2"]
}

当我直接调用它时效果很好:

final Gson gson = new GsonBuilder()
        .registerTypeAdapter(MyModel.class, new MyModelSerializer())
        .create();
String s = gson.toJson(new MyModel(MyModel.ActionName.ACCEPT, new ArrayList<String>()));
Log.d("tag", s);

我得到 {"accept":[]}。但是当我用 Retrofit 调用它时,我在日志中看到:D/OkHttp: name=abc&items=ru.bartwell.myapp.MyModel%4010a3d8b

我使用此代码提出请求:

try {
    MyModel myModel = new MyModel(MyModel.ActionName.ACCEPT, new ArrayList<String>());
    Response<ResultModel> response = getMyApi().getResult(1, "abc", myModel).execute();
    if (response.isSuccessful()) {
        ResultModel resultModel = response.body();
        // handle resultModel
    }
} catch (Exception e) {
    e.printStackTrace();
}

MyApi:

@FormUrlEncoded
@PUT("path/{id}")
Call<ResultModel> getResult(@Path("id") long item, @Field("name") @NonNull String name, @Field("items") @NonNull MyModel myModel);

getMyApi()方法:

public static MyApi getMyApi() {
    final Gson gson = new GsonBuilder()
        .registerTypeAdapter(MyModel.class, new MyModelSerializer())
        .create();

    return new Retrofit.Builder()
            .baseUrl(BuildConfig.END_POINT_MY_API)
            .client(MyOkHttpClient.create())
            .addConverterFactory(GsonConverterFactory.create(gson))
            .build()
            .create(MyApi.class);
}

型号:

public class MyModel {
    @NonNull
    private ActionName mActionName;
    @NonNull
    private List<String> mItems = new ArrayList<>();

    public MyModel(@NonNull final ActionName actionName, @NonNull final List<String> items) {
        mActionName = actionName;
        mItems = items;
    }

    @NonNull
    public ActionName getActionName() {
        return mActionName;
    }

    @NonNull
    public List<String> getItems() {
        return mItems;
    }

    public enum ActionName {
        ACCEPT("accept"), DECLINE("decline"), IGNORE("ignore");

        private String mName;

        ActionName(@NonNull final String name) {
            mName = name;
        }

        public String getName() {
            return mName;
        }
    }
}

序列化器:

public class MyModelSerializer implements JsonSerializer<MyModel> {
    @Override
    public JsonElement serialize(@NonNull MyModel src, @NonNull Type typeOfSrc, @NonNull JsonSerializationContext context) {
        JsonObject obj = new JsonObject();
        obj.add(src.getActionName().getName(), new Gson().toJsonTree(src.getItems()));
        return obj;
    }
}

如何解决?

发生这种情况是因为 @Field- 注释参数是用 stringConverter 序列化的。要覆盖默认行为,您只需添加另一个 Converter.Factory 并覆盖 stringConverter 方法:

.addConverterFactory(new Converter.Factory() {
    @Override
    public Converter<?, String> stringConverter(final Type type, final Annotation[] annotations, final Retrofit retrofit) {
        // Example check for supported classes
        if ( type instanceof Class ) {
            final Class<?> clazz = (Class<?>) type;
            if ( MyModel.class.isAssignableFrom(clazz) ) {
                return (Converter<Object, String>) value -> gson.toJson(value, type);
            }
        }
        return super.stringConverter(type, annotations, retrofit);
    }
})

那么您的查询参数字符串将如下所示:

name=abc&items=%7B%22accept%22%3A%5B%5D%7D

(这个 %7B%22accept%22%3A%5B%5D%7D' 是 URI 编码的 {"accept":[]})。

一些旁注:

  • Gson 实例是线程安全的,可以为整个应用程序创建一次(将单个实例传递给所有组件是完全没问题的)。
  • 你的 MyModelSerializer 可以改进:你不应该在那里创建一个 Gson 实例,因为反序列化器绑定到 Gson 实例配置,所以你必须通过给定上下文,如 context.serialize(myModel.mItems, itemsType),其中 itemsTypenew TypeToken<List<String>>() {}.getType().