Retrofit 去除请求参数中值为空字符串的字段

Retrofit 去除请求参数中值为空字符串的字段

需求

公司的开发中遇到这样一个需求:
在给后台发送的所有网络请求中,body参数中如果有字段的值是空字符串,就去掉这个字段,不发送给后台。
举个例子,假设我们要通过body发送的数据长这样:

{
    
    
	"param1":"1",
	"param2":"2",
	"param3":"3",
	"param4":"",
	"param5":null,
}

实际发送的参数:
修改前:

{
    
    
	"param1":"1",
	"param2":"2",
	"param3":"3",
	"param4":""
}

修改后:

{
    
    
	"param1":"1",
	"param2":"2",
	"param3":"3"
}

从例子中可以看出,我们要实现的功能就是在Retrofit发送请求的时候,将body中的值为空串的参数去掉。
在指定ConverterFactory为Gson之后,Retrofit默认会把值为 null 的参数去掉,但是并不会去掉值为空串的参数,所以我们要自己处理,去掉值为空串的参数。

指定ConverterFactory为默认的GsonConverterFactory的代码:

 Retrofit retrofit = new Retrofit.Builder()
           .baseUrl("https://wwww.baidu.com")
           .addConverterFactory(GsonConverterFactory.create())
           .build();

实现

解决办法其实不难,就是利用Retrofit的Converter机制。
我们项目中利用的是GsonConverter:

    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'

也就是说,我们的对象的序列化与反序列化是使用Gson的对象。那么下一步自然就是想到如何通过配置GsonConverter来实现这个需求。
其实这个需求实现起来很简单,代码如下:

		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();

关键代码如代码中注释所述。
这里的思路就是,指定GsonTypeAdapter,为String类型的变量单独指定序列化与反序列化的规则。
至于这个String类型的TypeAdapter的写法,实际上是参考了Gson对象的源码。

思考

初遇到这个需求的时候,我大概知道方向,但是具体的细节我也是不知道要如何实现的。我只知道Retrofit可以自己定义Converter,帮助我们实现序列化与反序列化。但是具体怎么去实现不去序列化String类型的空串,我也是不知道的。
我的思路是:
首先,GsonConverterFactory.create()方法有一个带参数的方法,GsonConverterFactory.create(Gson gson),那我们就可以通过为Factory配置实现了我们需求的gson对象来实现这个需求。
其次Gson默认不序列化null类型的对象。这是如何实现的?如果搞清楚了这个,我感觉就可以用同样的方式让Gson不序列化空串。
然后,基于上面的想法,我开始看Gson的源代码。看到了下面这一段:

public static final TypeAdapterFactory STRING_FACTORY = newFactory(String.class, STRING);

这里定义了一个TypeAdapterFactory,明显是用来处理String类型的。后面传入newFactory方法中的STRING对象的定义如下:

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);
    }
  };

看到这里,终于知道,其实我们要做的就是重新定义这里的 write 方法中的内容。
由于Gson默认是不序列化null的,所以我们可以在字符串为空时,指定输出一个null值。这样在默认情况下,就可以达到不输出空串的目标了。

篇幅有限,实际上自己看的和查的要比上面写的内容多很多,但是上面的内容是关键点,其他的细枝末节相信大家以自己的聪明才智也可以搞得定。因为Gson的源码本身难度并不高。

猜你喜欢

转载自blog.csdn.net/awy1988/article/details/114107308