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,示例代码:
替换原有代码里的Gson实例后,原有测试逻辑即可正常读写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(); - 方案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
相关产品推荐
相关产品推荐

