如何修复Jackson JSON解析错误:无法构造java.time.LocalDate实例
1. 概述
在使用 Java 处理 JSON 时,Jackson 通常无需过多配置就能处理 Java 8 的日期和时间类型。然而,我们仍然会遇到一些问题,例如常见的反序列化解析错误之一:
在较新的 Jackson 版本中,错误文本会以另一种形式出现:
尽管措辞略有不同,但两种错误都表明 Jackson 无法将 JSON 值转换为 LocalDate 或 LocalDateTime。
在本教程中,我们将探讨这个 JSON 反序列化异常及其解决方法。首先,我们用一个示例来演示问题。随后,我们会逐一分析导致该问题的不同原因以及如何处理它们。
2. 理解问题
我们编写一个基本的 User 类:
现在,假设我们收到一段匹配的 JSON:
Jackson 并没有成功反序列化这段 JSON,而是抛出类似下面的异常:
值得注意的是,JSON 数据看起来是有效的,因为日期已经遵循 LocalDate 所期望的 ISO-8601 格式。问题通常不在 JSON 本身,而在于 Jackson 的配置。事实上,根本原因可能是缺少 Java Time 模块、日期格式不受支持,或者为传入的 JSON 使用了错误的 Java 日期时间类型。
3. 验证 Jackson 版本
Jackson 各版本对 Java Time API 的支持发生了显著变化。在深入探讨具体技术问题和代码变更之前,我们不妨检查一下所使用的版本。
在 Jackson 2.x 中,Java Time 支持由单独的 jackson-datatype-jsr310 模块提供。在 Jackson 3.x 中,该支持已内置于 jackson-databind,因此不需要单独注册模块。
如果项目使用的是过时的 Jackson 版本,升级 Jackson 可能会在没有代码改动的情况下解决反序列化问题。对于 Jackson 2.x,我们还应该确认 Java Time 模块是否存在并已正确配置。
4. 确保 Java Time 支持
导致这个 JSON 错误的最常见原因是 Jackson 配置不支持 Java 8 日期和时间 API。
与 java.util.Date 等较旧的类型不同,LocalDate、LocalDateTime 和 Instant 等类是通过 Jackson Java Time 模块提供的。
如果缺少该模块,Jackson 会尝试像实例化普通 Java 对象一样实例化 LocalDate。由于 LocalDate 没有默认构造函数或字符串构造函数,反序列化就会失败。
因此,假设我们决定使用 Jackson 2.x,让我们引入 Java Time 模块依赖:
如果手动创建 ObjectMapper,则应注册该模块:
不过,Spring Boot 会在类路径中存在该模块时自动完成注册。
5. 匹配 JSON 日期格式
导致 JSON 反序列化错误的另一个常见原因是确切的日期格式。如果传入的 JSON 没有使用 LocalDate 和 LocalDateTime 默认期望的 ISO-8601 格式,我们可能会看到错误。
例如,让我们看一段无法自动解析的 JSON 字符串:
注册了 Java Time 模块后,Jackson 默认期望 LocalDateTime 字段使用 ISO-8601 日期时间格式:
在这种情况下,当 JSON 数据使用其他格式时,字段需要指定期望的格式模式:
不过,这个例子还包含了另一个提示。JSON 数据实际上可以同时包含日期和时间,而 Java 的 LocalDate 字段只存储日期。
6. 使用正确的 Java 时间类型
LocalDate 只表示日历日期。特别是,它不包含任何一天中的时间信息。
如果 JSON 数据包含小时、分钟或秒,LocalDate 就不再是合适的目标类型:
相反,我们可能应该使用 LocalDateTime:
此外,可以通过 @JsonFormat 声明匹配的格式:
选择能够准确表示 JSON 载荷的 Java 类型往往是最简单的解决方案。
7. LocalDateTime 也有类似要求
使用 LocalDateTime 时,我们经常会遇到相同类型的异常:
让我们考虑下面的 JSON:
和之前一样,由于该格式不同于默认的 ISO-8601 表示,Jackson 需要显式指定格式模式:
ObjectMapper 还必须注册 Java Time 模块,但对于这种非 ISO 格式,仍然需要显式给出格式模式。
8. 对比
现在我们已经了解了使用 Jackson 解析日期时间 JSON 时出现错误的不同原因,下面是我们发现内容的概览:
因此,每当我们遇到意外错误时,都可以查阅这个参考表。
9. 结论
在本文中,我们探讨了 Jackson 无法构造 LocalDate 或 LocalDateTime 实例的错误。
具体来说,我们确认该错误通常表明 Jackson 无法确定如何将传入的 JSON 转换为 Java 日期时间对象。
总而言之,一旦 Java 类型、JSON 载荷格式和 Jackson 配置保持一致,这类反序列化错误就会消失,Java Time 类型也应该能被透明地处理。