Retrofit2:不会为@ Field-annotated参数调用序列化程序

时间:2017-04-07 11:08:05

标签: android gson retrofit2 okhttp3

我需要在请求的参数之一中发送带有自定义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;
    }
}

如何解决?

1 个答案:

答案 0 :(得分:1)

这是因为@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);
    }
})

然后您的查询参数字符串将如下所示:

  

名= ABC&安培;项=%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()