
Gson 默认会将 JSON 中的 null 值直接赋给字段,导致集合类型字段为 null;本文介绍通过自定义 TypeAdapter 实现「null → 空集合」的统一转换策略,避免为每个类重复编写逻辑,同时对比 Jackson 的原生支持方案。
gson 默认会将 json 中的 `null` 值直接赋给字段,导致集合类型字段为 null;本文介绍通过自定义 typeadapter 实现「null → 空集合」的统一转换策略,避免为每个类重复编写逻辑,同时对比 jackson 的原生支持方案。
在使用 Gson 进行 JSON 反序列化时,若目标字段声明为 List
根本原因在于:Gson 不会跳过 null 字段的赋值,即使字段已在类中声明了非 null 的默认值(如 private final List
✅ 正确解决方案:注册泛型 Collection TypeAdapter
无需为每个集合类型单独注册适配器,只需一个通用 TypeAdapter 即可覆盖所有 Collection 子类型(List、Set、Queue 等):
import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.*;
public class EmptyCollectionTypeAdapterFactory implements TypeAdapterFactory {
@Override
public <t> TypeAdapter<t> create(Gson gson, TypeToken<t> typeToken) {
Type type = typeToken.getType();
if (type instanceof Class && Collection.class.isAssignableFrom((Class>) type)) {
// 使用原始类型适配器,避免泛型擦除问题
return (TypeAdapter<t>) new CollectionTypeAdapter(gson);
}
return null;
}
private static class CollectionTypeAdapter extends TypeAdapter<collection>> {
private final Gson gson;
CollectionTypeAdapter(Gson gson) {
this.gson = gson;
}
@Override
public void write(JsonWriter out, Collection> value) throws IOException {
if (value == null) {
out.nullValue();
} else {
gson.getAdapter(Collection.class).write(out, value);
}
}
@Override
public Collection> read(JsonReader in) throws IOException {
JsonElement element = JsonParser.parseReader(in);
if (element.isJsonNull()) {
return new ArrayList(); // 统一返回空 ArrayList,也可按需返回 LinkedHashSet 等
}
// 非 null 时委托给默认适配器解析
return gson.fromJson(element, Collection.class);
}
}
}</collection></t></t></t></t>
使用方式(全局生效):
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(new EmptyCollectionTypeAdapterFactory())
.create();
String json = "{\"userName\":\"test\",\"eMailAddress\":\"test@example.com\",\"list\":null}";
User user = gson.fromJson(json, User.class);
System.out.println(user.getList()); // 输出: []
⚠️ 注意事项:
- 该适配器仅作用于 Collection 接口及其子类型(List/Set/Deque),不适用于数组或 Map;
- 若需支持 Map,需额外注册 Map 专用适配器;
- final 字段仍需确保 Gson 有写入权限(推荐使用 @SerializedName + 无参构造器,或启用 GsonBuilder().excludeFieldsWithoutExposeAnnotation() 配合 @Expose 控制);
- 不建议在 deserialize() 中递归调用 new Gson().fromJson(...)(如原问题中的错误示例),会导致无限递归或类型丢失。
? 替代方案:改用 Jackson(更简洁)
Jackson 原生支持此行为:只要字段已初始化(如 private List
import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.annotation.Nulls;
public class User {
private String userName;
private String email;
private List<object> list = new ArrayList();
@JsonSetter(nulls = Nulls.SKIP)
public void setList(List<object> list) {
this.list = list != null ? list : new ArrayList();
}
}</object></object>
但若坚持使用 Gson,上述 TypeAdapterFactory 是最健壮、可复用的工程级解法——它集中处理所有集合类型,零侵入原有模型类,且兼容泛型。
总结:Gson 的 null 集合问题本质是设计取舍,而非缺陷。通过 TypeAdapterFactory 统一拦截并转换,既能保持代码简洁性,又能彻底规避 NPE 风险,是微服务、DTO 层 JSON 解析场景下的推荐实践。











