Retrofit removes fields with empty strings in request parameters

Retrofit removes fields with empty strings in request parameters

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 nullof 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 Gsona TypeAdapterfor the Stringspecified serialization and deserialization rule variable type alone.
As for the writing of this Stringtype TypeAdapter, it actually refers to Gsonthe 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 Retrofityou can define it yourself to Converterhelp us achieve serialization and deserialization. But StringI 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 Factoryconfiguring the gsonobject that realizes our requirement.
Second , objects Gsonof the nulltype are not serialized by default . How is this achieved? If I figure this out, I feel that I can use the same way to Gsonunserialize the empty string.
Then , based on the above ideas, I started to look at Gsonthe 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 Stringtypes. The definition newFactoryof the STRINGobject 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 writecontent method.
Since Gson is not serialized by default null, we can specify to output a nullvalue 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.

Guess you like

Origin blog.csdn.net/awy1988/article/details/114107308