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)
,其中 itemsType
是 new TypeToken<List<String>>() {}.getType()
.
我需要在请求参数之一中发送带有自定义 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)
,其中itemsType
是new TypeToken<List<String>>() {}.getType()
.