You need to enable JavaScript to run this app. Android 使用 Gson 序列化 LocalDate 存入 SharedPreferences 出现异常
优惠活动
大模型
产品
解决方案
定价
更多

Android 使用 Gson 序列化 LocalDate 存入 SharedPreferences 出现异常

问题成因

Gson的默认序列化逻辑和java.time.LocalDate的类结构存在本质不兼容,是异常的核心来源:

  • java.time.LocalDate是Java 8引入的不可变时间类,内部实际存储年、月、日数值的字段均为私有,且不存在Gson默认反射机制可以扫描到的可访问实体字段。Gson序列化过程中无法提取到任何有效日期值,最终只会生成空JSON对象{},对应日志中TestDate String after: {}的输出。
  • 反序列化阶段,Gson拿到空的{}对象后,尝试通过反射为LocalDate的内部字段填充默认值,因为没有接收到任何实际日期参数,年、月、日字段全部被填充为0,最终生成值为0000-00-00的无效LocalDate实例,和日志里的反序列化输出完全吻合。
解决方案

可以根据项目场景选择以下任意一种可直接落地的方案:

  • 方案1:给Gson注册LocalDate专用类型适配器(推荐全局使用)
    绕开Gson默认的反射序列化逻辑,自定义专门处理LocalDate序列化/反序列化的规则,序列化时直接输出LocalDate自带的ISO格式yyyy-MM-dd字符串,反序列化时直接调用解析方法把字符串转回LocalDate,示例代码:
    // 构建适配LocalDate的Gson实例,替代项目里默认的new Gson()
    Gson gson = new GsonBuilder()
        .registerTypeAdapter(LocalDate.class, new TypeAdapter<LocalDate>() {
            @Override
            public void write(JsonWriter out, LocalDate value) throws IOException {
                out.value(value == null ? null : value.toString());
            }
            @Override
            public LocalDate read(JsonReader in) throws IOException {
                if (in.peek() == JsonToken.NULL) {
                    in.nextNull();
                    return null;
                }
                return LocalDate.parse(in.nextString());
            }
        })
        .create();
    
    替换原有代码里的Gson实例后,原有测试逻辑即可正常读写LocalDate,不会出现空对象或无效日期的问题。
  • 方案2:在实体类层做字段适配(适合局部场景)
    如果不想改动全局Gson配置,可以在包含LocalDate字段的自定义类(比如你的Task类)上做适配,屏蔽Gson对LocalDate本身的序列化,转而存储可被Gson正常处理的ISO日期字符串,在字段的get/set方法里自动完成类型转换,示例代码:
    public class Task {
        // 禁止Gson直接序列化LocalDate字段
        @Expose(serialize = false, deserialize = false)
        private LocalDate taskDate;
        // 实际序列化存储的ISO格式日期字符串
        private String taskDateStr;
    
        public LocalDate getTaskDate() {
            return taskDateStr == null ? null : LocalDate.parse(taskDateStr);
        }
        public void setTaskDate(LocalDate taskDate) {
            this.taskDate = taskDate;
            this.taskDateStr = taskDate == null ? null : taskDate.toString();
        }
    }
    
  • 方案3:绕开Gson处理LocalDate的逻辑
    读写SharedPreferences时,直接把LocalDate转成ISO格式字符串单独存储,读取时再调用LocalDate.parse()转回对象,不依赖Gson完成LocalDate类型的转换。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.28 01:45:02