问题背景
之前我写过一篇文章《Gson对字符串null的字段转换为空字符串输出》,有个兄弟评论说:定义返回的对象,code,msg,object data类型 data类型里面如果是List的map好像还是转不了。。
上图代码的maps输出结果是:[{"id":"123"},{"id":"123"},{"id":"123"}]
看了上面的代码,不知道其他同学有什么想法?
我发现还是有人没有理解原理,如果不知道为什么,这时候debug源码很快就可以获得答案。
解析GSON适配器
Gson有自己适配器,所有map都会走默认的MapTypeAdapterFactory类。
在这个类里面write方法中,对value的判断调用valueTypeAdapter.write(out, entry.getValue());
方法。代码如下:
publicvoidwrite(JsonWriterout, Map<K, V>map) throwsIOException { 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方法:
publicvoidwrite(JsonWriterout, Objectvalue) throwsIOException { if (value==null) { out.nullValue(); return; } TypeAdapter<Object>typeAdapter= (TypeAdapter<Object>) gson.getAdapter(value.getClass()); if (typeAdapterinstanceofObjectTypeAdapter) { out.beginObject(); out.endObject(); return; } typeAdapter.write(out, value); }
因为value == null
,所以最后调用JsonWriter.nullValue();
publicJsonWriternullValue() throwsIOException { if (deferredName!=null) { if (serializeNulls) { writeDeferredName(); } else { deferredName=null; returnthis; // skip the name and the value } } beforeValue(); out.write("null"); returnthis; }
到这里大家就明白了吧,我们没有配置serializeNulls
,这里的serializeNulls == false
,所以后面分支的注释已经说了:如果对于null值,会把它的name置空,跳过,也就是最后不输出。
问题根因大家知道了,解决办法应该有好几个:
(1)最常用的就是自定义一个map的adapter,让Gson启动加载,上篇文章自定义的string的适配器不是map的适配器,所以是不起作用的。
(2)重写MapTypeAdapterFactory类。
(3)改写JsonWriter类。
因为Gson很多类都是final的,无法继承覆写,还是使用第一个比较简单。
下面是我写的自定义map适配器:
publicclassMyMapTypeAdapter<K, V>extendsTypeAdapter<Map<K, V>>{ publicvoidwrite(JsonWriterout, Map<K, V>map) throwsIOException { if (map==null) { out.nullValue(); return; } out.beginObject(); for (Map.Entry<K, V>entry : map.entrySet()) { out.name(String.valueOf(entry.getKey())); // 核心代码在这里,如果value是null,输出空字符串Objectvalue= (entry.getValue() ==null) ?"" : entry.getValue(); newGson().getAdapter(Object.class).write(out, value); } out.endObject(); return; } publicMap<K, V>read(JsonReadervar1) throwsIOException { // TODO Auto-generated method stubreturnnull; } }
运行结果[{"name":"","id":"123"},{"name":"","id":"123"},{"name":"","id":"123"}]
到这里,就解决了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技能和互联网面试技巧。问题或建议,请公众号留言。\
如果你觉得这篇文章对你有帮助,欢迎一键三连