You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android中Gson非基本类型字段默认值失效问题解决方案咨询

解决Gson解析时非基本类型字段保留默认值的问题

你遇到的情况确实是Gson的默认行为导致的:在ReflectiveTypeAdapterFactory.BoundField的read方法中,只有基本类型会在解析值为null时跳过赋值,引用类型则会直接将字段设为null,哪怕你已经在Model中初始化了默认值。下面提供几种实用的解决方案:

方案1:针对集合类型注册全局TypeAdapterFactory

如果你的需求主要是处理List这类集合的空值问题,可以编写一个全局的TypeAdapterFactory,让所有List类型字段在解析到null时自动返回空列表:

步骤1:编写EmptyListTypeAdapterFactory

public class EmptyListTypeAdapterFactory implements TypeAdapterFactory {
    @SuppressWarnings("unchecked")
    @Override
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        // 只处理List类型
        if (!List.class.isAssignableFrom(type.getRawType())) {
            return null;
        }
        // 获取Gson默认的TypeAdapter作为委托
        TypeAdapter<T> delegateAdapter = gson.getDelegateAdapter(this, type);
        return (TypeAdapter<T>) new EmptyListTypeAdapter<>((TypeAdapter<List<?>>) delegateAdapter);
    }

    private static class EmptyListTypeAdapter<E> extends TypeAdapter<List<E>> {
        private final TypeAdapter<List<E>> delegate;

        EmptyListTypeAdapter(TypeAdapter<List<E>> delegate) {
            this.delegate = delegate;
        }

        @Override
        public void write(JsonWriter out, List<E> value) throws IOException {
            delegate.write(out, value);
        }

        @Override
        public List<E> read(JsonReader in) throws IOException {
            List<E> list = delegate.read(in);
            // 如果解析结果为null,返回空ArrayList
            return list != null ? list : new ArrayList<>();
        }
    }
}

步骤2:注册到GsonBuilder

创建Gson实例时注册这个Factory,同时保留你的@Expose注解配置:

Gson gson = new GsonBuilder()
        .excludeFieldsWithoutExposeAnnotation()
        .registerTypeAdapterFactory(new EmptyListTypeAdapterFactory())
        .create();

这样所有List类型的字段,无论是JSON中不存在还是值为null,都会自动返回空列表,无需逐个字段处理。

方案2:自定义通用TypeAdapterFactory处理所有引用类型

如果你希望所有引用类型(不仅是List)都能保留默认值,可以编写一个更通用的Factory,当解析到null时,不覆盖Model中已初始化的字段值:

步骤1:编写DefaultValuePreserveAdapterFactory

这个Factory会修改Gson的默认赋值逻辑,让引用类型和基本类型一样,在解析值为null时跳过赋值:

public class DefaultValuePreserveAdapterFactory implements TypeAdapterFactory {
    @Override
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        // 只处理自定义Model类,跳过系统类
        if (type.getRawType().isPrimitive() || type.getRawType().isAssignableFrom(String.class)) {
            return null;
        }
        final TypeAdapter<T> delegateAdapter = gson.getDelegateAdapter(this, type);

        return new TypeAdapter<T>() {
            @Override
            public void write(JsonWriter out, T value) throws IOException {
                delegateAdapter.write(out, value);
            }

            @Override
            public T read(JsonReader in) throws IOException {
                // 先创建实例,保留初始化的默认值
                T instance = null;
                try {
                    instance = type.getRawType().newInstance();
                } catch (InstantiationException | IllegalAccessException e) {
                    throw new RuntimeException("Model类需要无参构造函数", e);
                }
                // 解析JSON元素
                JsonElement element = JsonParser.parseReader(in);
                if (element.isJsonObject()) {
                    JsonObject jsonObject = element.getAsJsonObject();
                    // 遍历所有带@Expose的字段
                    for (Field field : type.getRawType().getDeclaredFields()) {
                        if (field.isAnnotationPresent(Expose.class)) {
                            field.setAccessible(true);
                            String fieldName = field.getName();
                            if (jsonObject.has(fieldName)) {
                                JsonElement fieldElement = jsonObject.get(fieldName);
                                // 仅当字段值非null时,覆盖原有值
                                if (!fieldElement.isJsonNull()) {
                                    Object fieldValue = gson.fromJson(fieldElement, field.getType());
                                    field.set(instance, fieldValue);
                                }
                            }
                        }
                    }
                }
                return instance;
            }
        };
    }
}

步骤2:注册使用

同样注册到GsonBuilder即可,这个方案会让所有带@Expose注解的引用类型字段,在解析到null时保留初始化的默认值。

方案3:单个字段指定JsonAdapter

如果只有少数字段需要处理,可以直接在字段上使用@JsonAdapter注解,指定自定义TypeAdapter:

步骤1:编写单个字段的TypeAdapter

public class EmptyStringListAdapter extends TypeAdapter<List<String>> {
    private final TypeAdapter<List<String>> delegate = new Gson().getAdapter(TypeToken.get(List.class));

    @Override
    public void write(JsonWriter out, List<String> value) throws IOException {
        delegate.write(out, value);
    }

    @Override
    public List<String> read(JsonReader in) throws IOException {
        List<String> list = delegate.read(in);
        return list != null ? list : new ArrayList<>();
    }
}

步骤2:在Model中使用

class Model {
    @Expose private int test = 1;
    @Expose private int primitive = 1;
    @Expose 
    @JsonAdapter(EmptyStringListAdapter.class)
    private List<String> strings = new ArrayList<>();
}

注意事项

  • 确保你的Model类有无参构造函数,Gson默认依赖无参构造创建实例,否则初始化的默认值不会生效。
  • 如果Model是内部类,需要声明为static,否则Gson无法正确创建实例。

内容的提问来源于stack exchange,提问作者Vladyslav Komarov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 06:31:03