持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第27天,点击查看活动详情
今天在和后端联调接口的时候,接口请求体中有一个字段是非必需的,也就是有的场景下需要这个字段,有的场景下不需要。但是后端同学又要求不需要的情况下传一个null
过去。
也就是接口提交的数据需要如下所示:
{
"subject": null
}
行,既然这样要求,那就这样做吧。
项目中使用Retrofit2+RxJava进行网络请求。
定义接口请求方法:
@POST("xxx/activated_list")
fun fetchBookshelf(@Body request: BookshelfRequest): Observable<FetcherResponse<BookshelfResponse>>
请求体如下所示:
data class BookshelfRequest(val subject: Int? = null)
然后开始请求数据,在不需要传值的情况下请求数据:
presenter.fetchBookshelf(BookshelfRequest(null))
本以为万事大吉了,但是数据并没有正常请求,抓接口发现,提交的json数据中并没有subject这个字段:
{}
分析原因发现,是Gson
在将请求体BookshelfRequest
转成json串的时候,将值为null
的字段过滤了,没有参加序列化。
首先看下Retrofit的使用步骤。
创建Retrofit
,调用addConverterFactory()
方法传入一个转换工厂。
@Override
protected Retrofit createRetrofit(OkHttpClient okHttpClient) {
Retrofit retrofit = super.createRetrofit(okHttpClient);
return retrofit.newBuilder()
//这里会给Retrofit传一个自定义Converter.Factory,用来做数据实体和json串之间的转换
.addConverterFactory(CustomGsonConverterFactory.create(getResponseFilterValid()))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
CustomGsonConverterFactory
是自定义的ConverterFactory
,继承自Retrofit的Converter.Factory
。
public class CustomGsonConverterFactory extends Converter.Factory {
private final Gson mGson;
private final IResponseFilterValid mResponseFilterValid;
/**
* Create an instance using a default {@link Gson} instance for conversion. Encoding to JSON and
* decoding from JSON (when no charset is specified by a header) will use UTF-8.
*/
public static CustomGsonConverterFactory create(IResponseFilterValid responseFilterValid) {
//接口要求请求体的json串的字段值为null也要提交过去,就像这样{"subject": null}
//而Retrofit使用@Body提交入参后,Gson默认会过滤掉值为null的字段,所以这里需要给Gson加上.serializeNulls()方法
//表示值为mull的字段也会参与序列化,这样传递给后台的json串就是为{"subject": null}了
Gson gson = new GsonBuilder().serializeNulls().create();
return create(gson, responseFilterValid);
}
...//省略代码
}
可以看到这里创建了一个Gson
对象,解决本文中问题的关键就是调用一下GsonBuilder().serializeNulls()
方法。
看下GsonBuilder
的serializeNulls()
方法。
public GsonBuilder serializeNulls() {
this.serializeNulls = true;
return this;
}
调用serializeNulls()
之后,GsonBuilder()
中的serializeNulls
被赋值为true。然后通过GsonBuilder.create()
方法创建Gson
,serializeNulls
通过Gson
的构造方法传入Gson
类。
public Gson create() {
List<TypeAdapterFactory> factories = new ArrayList<TypeAdapterFactory>();
factories.addAll(this.factories);
Collections.reverse(factories);
factories.addAll(this.hierarchyFactories);
addTypeAdaptersForDate(datePattern, dateStyle, timeStyle, factories);
//通过GsonBuilder的create方法创建Gson,通过Gson的构造方法将serializeNulls传入
return new Gson(excluder, fieldNamingPolicy, instanceCreators,
serializeNulls, complexMapKeySerialization,
generateNonExecutableJson, escapeHtmlChars, prettyPrinting, lenient,
serializeSpecialFloatingPointValues, longSerializationPolicy, factories);
}
接着在Gson.toJson()
方法中,将serializeNulls
传入JsonWriter
。
public void toJson(Object src, Type typeOfSrc, JsonWriter writer) throws JsonIOException {
TypeAdapter<?> adapter = getAdapter(TypeToken.get(typeOfSrc));
boolean oldLenient = writer.isLenient();
writer.setLenient(true);
boolean oldHtmlSafe = writer.isHtmlSafe();
writer.setHtmlSafe(htmlSafe);
boolean oldSerializeNulls = writer.getSerializeNulls();
//将serializeNulls传入JsonWriter
writer.setSerializeNulls(serializeNulls);
try {
((TypeAdapter<Object>) adapter).write(writer, src);
} catch (IOException e) {
throw new JsonIOException(e);
} finally {
writer.setLenient(oldLenient);
writer.setHtmlSafe(oldHtmlSafe);
writer.setSerializeNulls(oldSerializeNulls);
}
}
然后在JsonWriter
的nullValue()
方法中,根据serializeNulls
的值去判断值为null
的字段参加不参加序列化。
public JsonWriter nullValue() throws IOException {
if (deferredName != null) {
if (serializeNulls) {
writeDeferredName();
} else {
deferredName = null;
return this; // skip the name and the value
}
}
beforeValue();
out.write("null");
return this;
}
所以,在创建Gson
的时候调用GsonBuilder().serializeNulls()
即可不过滤值为null
的字段。
关注木水小站 (zhangmushui.cn)和微信公众号【木水Code】,及时获取更多最新技术干货。