demand
The company’s development encountered such a requirement:
in all network requests sent to the background, if the value of a field in the body parameter is an empty string, this field is removed and not sent to the background.
For example, suppose the data we want to send through the body is as long as this:
{
"param1":"1",
"param2":"2",
"param3":"3",
"param4":"",
"param5":null,
}
Parameters actually sent:
Before modification:
{
"param1":"1",
"param2":"2",
"param3":"3",
"param4":""
}
After modification:
{
"param1":"1",
"param2":"2",
"param3":"3"
}
As can be seen from the example, the function we want to achieve is to remove the parameter whose value is an empty string in the body when Retrofit sends a request.
After specifying ConverterFactory as Gson, Retrofit will default value null
of the parameter to remove, but does not remove the parameter value is an empty string, so we have to deal with their own, remove the parameter value is an empty string.
Specify ConverterFactory as the default GsonConverterFactory code:
Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://wwww.baidu.com") .addConverterFactory(GsonConverterFactory.create()) .build();
achieve
The solution is actually not difficult, is to use Retrofit's Converter mechanism.
GsonConverter is used in our project:
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
In other words, the serialization and deserialization of our objects use Gson objects. So the next step is naturally to think of how to achieve this requirement by configuring GsonConverter.
In fact, this requirement is very simple to implement, the code is as follows:
TypeAdapter<String> stringTypeAdapter = new TypeAdapter<String>() {
@Override
public String read(JsonReader in) throws IOException {
JsonToken peek = in.peek();
if (peek == JsonToken.NULL) {
in.nextNull();
return null;
}
/* coerce booleans to strings for backwards compatibility */
if (peek == JsonToken.BOOLEAN) {
return Boolean.toString(in.nextBoolean());
}
return in.nextString();
}
@Override
public void write(JsonWriter out, String value) throws IOException {
// 下面这个if是关键代码,指定序列化时,遇到空串则直接输出null值。
// 由于Gson默认是不序列化null的,所以这里就相当于在序列化时排除了空串的字段
if ("".equals(value)) {
out.nullValue();
return;
}
out.value(value);
}
};
Gson gson = new GsonBuilder().registerTypeAdapter(String.class, stringTypeAdapter).create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://wwww.baidu.com")
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
The key code is as described in the comments in the code.
Here the idea is to specify Gson
a TypeAdapter
for the String
specified serialization and deserialization rule variable type alone.
As for the writing of this String
type TypeAdapter
, it actually refers to Gson
the source code of the object.
Thinking
When I first encountered this requirement, I probably knew the direction, but I didn't know how to achieve the specific details. I only know that Retrofit
you can define it yourself to Converter
help us achieve serialization and deserialization. But String
I don't know how to implement the empty string of the type that does not deserialize.
My idea is:
First of all , the GsonConverterFactory.create() method has a method with parameters GsonConverterFactory.create(Gson gson)
, then we can achieve this requirement by Factory
configuring the gson
object that realizes our requirement.
Second , objects Gson
of the null
type are not serialized by default . How is this achieved? If I figure this out, I feel that I can use the same way to Gson
unserialize the empty string.
Then , based on the above ideas, I started to look at Gson
the source code. I saw the following paragraph:
public static final TypeAdapterFactory STRING_FACTORY = newFactory(String.class, STRING);
One is defined here TypeAdapterFactory
, which is obviously used to deal with String
types. The definition newFactory
of the STRING
object passed in the method later is as follows:
public static final TypeAdapter<String> STRING = new TypeAdapter<String>() {
@Override
public String read(JsonReader in) throws IOException {
JsonToken peek = in.peek();
if (peek == JsonToken.NULL) {
in.nextNull();
return null;
}
/* coerce booleans to strings for backwards compatibility */
if (peek == JsonToken.BOOLEAN) {
return Boolean.toString(in.nextBoolean());
}
return in.nextString();
}
@Override
public void write(JsonWriter out, String value) throws IOException {
out.value(value);
}
};
See here, finally we know that we have to do here is to re-define the write
content method.
Since Gson is not serialized by default null
, we can specify to output a null
value when the string is empty . In this way, by default, you can achieve the goal of not outputting an empty string.
The space is limited. In fact, what I have read and checked is much more than the content written above, but the content above is the key point. I believe you can handle the other details with your own ingenuity. Because Gson's source code itself is not difficult.