如何使用Jackson反序列化JSON日期字段为Date或Joda DateTime
问题说明
接口返回的JSON中包含如下格式的Joda Time序列化日期字段:
{ "modifiedDate": { "dayOfWeek": 2, "dayOfYear": 172, "year": 2022, "dayOfMonth": 21, "era": 1, "monthOfYear": 6, "millisOfDay": 12144237, "hourOfDay": 3, "minuteOfHour": 22, "secondOfMinute": 24, "millisOfSecond": 237, "weekOfWeekyear": 25, "weekyear": 2022, "yearOfEra": 2022, "yearOfCentury": 22, "centuryOfEra": 20, "secondOfDay": 12144, "minuteOfDay": 202, "zone": { "fixed": true, "id": "Etc/UTC" }, "chronology": { "zone": { "fixed": true, "id": "Etc/UTC" } }, "millis": 1655781744237, "afterNow": false, "beforeNow": true, "equalNow": false } }
需要将该字段解析为java.util.Date或JodaDateTime类型。自定义反序列化器无法通过Jackson完成映射,相同结构JSON使用Gson可正常解析。
原因说明
- Gson默认按字段匹配策略做反序列化,只要JSON结构和目标类的可访问字段/构造参数匹配就能完成对象转换,因此可以直接识别该结构生成DateTime实例。
- Jackson默认的日期反序列化逻辑仅支持时间戳、标准格式日期字符串的转换,不会自动将嵌套的日期属性对象映射为Date/DateTime实例;自定义反序列化器失效通常是因为没有正确读取JSON节点中的核心时间字段。
实现方案
方案1:使用官方适配模块(无自定义代码,推荐)
Joda Time官方提供了Jackson的适配扩展包,可直接识别该序列化结构,无需手写反序列化逻辑:
- 引入和项目Jackson版本一致的适配依赖
<dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-joda</artifactId> <version>${jackson.version}</version> </dependency>
- 为ObjectMapper注册Joda模块并关闭时间戳序列化配置
ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(new JodaModule()); objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
- 实体类直接声明对应字段类型即可完成解析
public class BizEntity { private DateTime modifiedDate; // 其余字段、getter/setter省略 }
需要java.util.Date类型时,直接调用解析得到的DateTime实例的toDate()方法即可转换。
方案2:手写自定义反序列化器
如果不希望引入额外依赖,可通过读取日期结构中millis字段存储的UTC毫秒时间戳实现反序列化,不需要解析所有日期属性字段。
- 解析为
java.util.Date的反序列化器实现
public class CustomDateDeserializer extends StdDeserializer<Date> { public CustomDateDeserializer() { super(Date.class); } @Override public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { JsonNode dateNode = p.getCodec().readTree(p); long utcMillis = dateNode.get("millis").asLong(); return new Date(utcMillis); } }
- 解析为Joda
DateTime的反序列化器实现
public class CustomDateTimeDeserializer extends StdDeserializer<DateTime> { public CustomDateTimeDeserializer() { super(DateTime.class); } @Override public DateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { JsonNode dateNode = p.getCodec().readTree(p); long utcMillis = dateNode.get("millis").asLong(); String zoneId = dateNode.get("zone").get("id").asText(); return new DateTime(utcMillis, DateTimeZone.forID(zoneId)); } }
将实现好的反序列化器通过ObjectMapper全局注册,或在对应字段上添加@JsonDeserialize(using = 对应反序列化器类.class)注解即可生效。
注意:直接读取
millis字段是最稳定的实现方式,该字段是Joda序列化时存储的核心时间戳,和其余日历字段完全对齐,不会出现时区或日期计算偏差。
内容的提问来源于stack exchange,提问作者Ankur Mukherjee
相关产品推荐
相关产品推荐

