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

举报
Java实用技术@Pandas 发表于 2023/03/18 20:37:02 2023/03/18
【摘要】 之前我写过一篇文章《Gson对字符串null的字段转换为空字符串输出》,有个兄弟评论说:定义返回的对象,code,msg,object data类型 data类型里面如果是List的map好像还是转不了。。

问题背景

之前我写过一篇文章《Gson对字符串null的字段转换为空字符串输出》,有个兄弟评论说:定义返回的对象,code,msg,object data类型 data类型里面如果是List的map好像还是转不了。。

image.png

上图代码的maps输出结果是:[{"id":"123"},{"id":"123"},{"id":"123"}]

看了上面的代码,不知道其他同学有什么想法?

我发现还是有人没有理解原理,如果不知道为什么,这时候debug源码很快就可以获得答案。

解析GSON适配器

Gson有自己适配器,所有map都会走默认的MapTypeAdapterFactory类。

在这个类里面write方法中,对value的判断调用valueTypeAdapter.write(out, entry.getValue());方法。代码如下:

@Override 
public void write(JsonWriter out, Map<K, V> map) throws IOException {
      if (map == null) {
        out.nullValue();
        return;
      }

      if (!complexMapKeySerialization) {
        out.beginObject();
        for (Map.Entry<K, V> entry : map.entrySet()) {
          out.name(String.valueOf(entry.getKey()));
          valueTypeAdapter.write(out, entry.getValue());
        }
        out.endObject();
        return;
      }

      //...
}

因为map的泛型在开始时并不知道key和value的类型是什么,所以在MapTypeAdapterFactory类初始化的时候,valueAdapter实际上是ObjectTypeAdapter方法。我们看下具体write方法:

@Override 
public void write(JsonWriter out, Object value) throws IOException {
    if (value == null) {
      out.nullValue();
      return;
    }

    TypeAdapter<Object> typeAdapter = (TypeAdapter<Object>) gson.getAdapter(value.getClass());
    if (typeAdapter instanceof ObjectTypeAdapter) {
      out.beginObject();
      out.endObject();
      return;
    }

    typeAdapter.write(out, value);
  }

因为value == null,所以最后调用JsonWriter.nullValue();

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

到这里大家就明白了吧,我们没有配置serializeNulls,这里的serializeNulls == false,所以后面分支的注释已经说了:如果对于null值,会把它的name置空,跳过,也就是最后不输出。

问题根因大家知道了,解决办法应该有好几个:

(1)最常用的就是自定义一个map的adapter,让Gson启动加载,上篇文章自定义的string的适配器不是map的适配器,所以是不起作用的。

(2)重写MapTypeAdapterFactory类。

(3)改写JsonWriter类。

因为Gson很多类都是final的,无法继承覆写,还是使用第一个比较简单。

下面是我写的自定义map适配器:

public class MyMapTypeAdapter<K, V> extends TypeAdapter<Map<K, V>>
{

    @Override
    public void write(JsonWriter out, Map<K, V> map) throws IOException
    {
        if (map == null)
        {
            out.nullValue();
            return;
        }

        out.beginObject();
        for (Map.Entry<K, V> entry : map.entrySet())
        {
            out.name(String.valueOf(entry.getKey()));
            
            // 核心代码在这里,如果value是null,输出空字符串
            Object value = (entry.getValue() == null) ? "" : entry.getValue();
            
            new Gson().getAdapter(Object.class).write(out, value);
        }
        out.endObject();
        return;

    }

    @Override
    public Map<K, V> read(JsonReader var1) throws IOException
    {
        // TODO Auto-generated method stub
        return null;
    }

}

运行结果[{"name":"","id":"123"},{"name":"","id":"123"},{"name":"","id":"123"}]

image.png

到这里,就解决了Map的value中为空就不输出的问题。

思考

上面有个细节,就是为啥Gson不对ObjectTypeAdapter中的null值提供写为""的方法?

因为我们常说的null其实是一种像量子态的引用类型,你可以说它没有类型,但是你又可以把它转为任何类型。

Gson不知道你传给它的null到底是(String)null,还是(Integer)null,也许String类型的null想变成"",也许Integer类型的null就想变成0。所以自由权还是给使用者,自己定义吧。

如果有人使用阿里的fastjson,对于map的null值处理也要自定义filter才行,默认的SerializerFeature.WriteNullStringAsEmpty不行。

备注:本文使用的 Gson 版本为 gson-2.5

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

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

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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