Gson对字符串null的字段转换为空字符串输出

举报
Java实用技术@Pandas 发表于 2023/03/31 08:15:04 2023/03/31
【摘要】 最近有个项目需要将后端数据以json方式传给前端。但是如果后端有字段为null,使用原始的new Gson()就排除为null的字段,不传给前端。缺少字段前端会报错。于是就按照网上方法使用了new GsonBuilder().serializeNulls()。好吧,现在每个字段都能传给前端了,但是有个潜在不足,就是为null的字段实际变成了"null"字符串给前端了。

     最近有个项目需要将后端数据以json方式传给前端。但是如果后端有字段为null,使用原始的new Gson()就排除为null的字段,不传给前端。缺少字段前端会报错。于是就按照网上方法使用了new GsonBuilder().serializeNulls()。好吧,现在每个字段都能传给前端了,但是有个潜在不足,就是为null的字段实际变成了"null"字符串给前端了。


    对于严谨的程序员来说,这个不能忍。于是就想找到一个更完美的方法。


    先说下网上目前存在的两种方法:

    1、写一个NullStringToEmptyAdapterFactory,然后使用new GsonBuilder().registerTypeAdapterFactory(new NullStringToEmptyAdapterFactory())。

    2、new TypeAdapter内部类,然后使用new GsonBuilder().registerTypeAdapter(String.class, STRING)。

    

    然后我都试了,方法一却根本没有效果,方法二还报错。


    如果你试了上面两个中的任意一种方法都成功了,恭喜你,可以直接进入源码分析阶段。

    如果你不幸地和我一样,还是不行,我想你可能跟我一样,遇到gson jar包问题!有问题的是gson-2.2.1.jar版本。

    

    研究了一下源码,找到了上面两个方法都不行的原因。

    

  public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter)
  {
    Gson.Preconditions.checkArgument(((typeAdapter instanceof JsonSerializer)) || ((typeAdapter instanceof JsonDeserializer)) || ((typeAdapter instanceof InstanceCreator)) || ((typeAdapter instanceof TypeAdapter)));

    if ((Primitives.isPrimitive(type)) || (Primitives.isWrapperType(type)) || (type == String.class)) {
      throw new IllegalArgumentException("Cannot register type adapters for " + type);
    }

    if ((typeAdapter instanceof InstanceCreator)) {
      this.instanceCreators.put(type, (InstanceCreator)typeAdapter);
    }
    if (((typeAdapter instanceof JsonSerializer)) || ((typeAdapter instanceof JsonDeserializer))) {
      TypeToken typeToken = TypeToken.get(type);
      this.factories.add(TreeTypeAdapter.newFactoryWithMatchRawType(typeToken, typeAdapter));
    }
    if ((typeAdapter instanceof TypeAdapter)) {
      this.factories.add(TypeAdapters.newFactory(TypeToken.get(type), (TypeAdapter)typeAdapter));
    }
    return this;
  }


方法二报错是因为源码不允许用户自定义基本类型和封装类型。方法一虽然注册了adapter工厂,谷歌依然没有使用自定义的adapter。


if (!Primitives.isPrimitive(type) && !Primitives.isWrapperType(type) && type != String.class)
{
}
else
{
throw new IllegalArgumentException("Cannot register type adapters for " + type);
}


在gson-2.2.2以后的版本就已经没有了上面的代码,允许注册自定义adapter。

我升级了gson版本,使用gson-2.5,但是依然没有输出空字符串,于是我又去看源码了。

一开始我参考某篇文章,在public void write(JsonWriterwriter,Stringvalue)中写了如下代码:


if (value == null)
{
    writer.nullValue();
    return;
}


这就是问题所在。

序列化使用的是adapter的write方法,反序列化使用的是read方法,所以在write方法里应该写


if (value == null)
{
    writer.value("");
    return;
}


终于解决了问题。

    

    现在来总结一下怎么完美地解决null字段以空字符串输出。只需要定义一个静态adapter常量,注册到GsonBuilder即可。网上那些定义NullStringToEmptyAdapterFactory和StringNullAdapter类根本就不必要。

    为什么?源代码中写了,自定义adapter后,gson会再构造成adapterFactory注册的。


if(typeAdapter instanceof TypeAdapter)
{
     this.factories.add(TypeAdapters.newFactory(TypeToken.get(type), (TypeAdapter)typeAdapter));
}


最后给一下最终代码样例:

public class TestMain
{
    //自定义Strig适配器
	private static final TypeAdapter
   
     STRING = new TypeAdapter()
	 {
		public String read(JsonReader reader) throws IOException
		{
			if (reader.peek() == JsonToken.NULL)
			{
				reader.nextNull();
				return "";
			}
			return reader.nextString();
		}
		
		public void write(JsonWriter writer, String value) throws IOException
		{
			if (value == null)
			{
				// 在这里处理null改为空字符串
				writer.value("");
				return;
			}
			writer.value(value);
		}
	};
	
	public static void main(String[] args)
	{
		GsonBuilder gsonBuilder = new GsonBuilder();
		
		//注册自定义String的适配器
		gsonBuilder.registerTypeAdapter(String.class, STRING);
		
		Gson gson = gsonBuilder.create();
		UserBean userBean = new UserBean();
		userBean.setId("1");
		System.out.println(gson.toJson(userBean));
	}
}



微信公众号:Java实用技术手册
关注可了解更多java技能和互联网面试技巧。问题或建议,请公众号留言。
如果你觉得这篇文章对你有帮助,欢迎一键三连

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。