ZonedDatatime的Jackson序列化问题
最近测试接口需要用到Jackson序列化和反序列化,其中发现对于时间类型的变量Jackson的序列化过程和反序列化过程不可逆,即序列化产生的结果无法被反序列化解析回ZonedDatatime对象,经过一番调整后问题解决,现将过程记录如下。
使用Jackson模块对ZonedDatetime序列化后产生结果的形式如下
{
"offset": {
"totalSeconds": 25200,
"rules": {
"fixedOffset": true,
"transitionRules": [
],
"transitions": [
],
"class": "java.time.zone.ZoneRules"
},
"id": "+07:00",
"class": "java.time.ZoneOffset"
},
"year": 2020,
"dayOfYear": 181,
"nano": 364000000,
"chronology": {
"calendarType": "iso8601",
"id": "ISO",
"class": "java.time.chrono.IsoChronology"
},
"minute": 15,
"second": 38,
"dayOfWeek": "MONDAY",
"month": "JUNE",
"hour": 18,
"dayOfMonth": 29,
"zone": {
"rules": {
"fixedOffset": true,
"transitionRules": [
],
"transitions": [
],
"class": "java.time.zone.ZoneRules"
},
"id": "GMT+07:00",
"class": "java.time.ZoneRegion"
},
"monthValue": 6,
"class": "java.time.ZonedDateTime"
}
但是这个结果用Jackson进行反序列化后,类型为LocakDateTime的dataTime字段会被变成null。可以看到文本中也确实没有dateTime字段。
尝试手动进行LocalDateTime字段的转换
直接对一个LocalDatetime进行Jackson序列化
public static void main(String[] args) throws JsonProcessingException {
LocalDateTime time= LocalDateTime.now();
String json=(new ObjectMapper()).writeValueAsString(time);
System.out.println(json);
}
显示的结果如下
{
"dayOfMonth":7,
"dayOfWeek":"TUESDAY",
"dayOfYear":189,
"hour":16,
"minute":44,
"month":"JULY",
"monthValue":7,
"nano":743000000,
"second":54,
"year":2020,
"chronology":{
"id":"ISO",
"calendarType":"iso8601"
}
}
对比上方的ZonedDateTime的序列化结果可以发现dateTime内的字段又被提了出来,所以将这些字段疯传到"dateTime"中似乎可以解决问题。但是之后又显示没有date对象。
LocalDate的序列化
阅读JDK源代码中的LocalDateTime发现
/**
* The date part.
*/
private final LocalDate date;
/**
* The time part.
*/
private final LocalTime time;
推测是LocalDateTime中同样出现了字段被拉平的问题,于是对LocalDate对象进行序列化,结果如下
public static void main(String[] args) throws JsonProcessingException {
LocalDate time=LocalDate.now();
String json=(new ObjectMapper()).writeValueAsString(time);
System.out.println(json);
}
果不其然,结果如下
{
"year":2020,
"month":"JULY",
"chronology":{
"id":"ISO",
"calendarType":"iso8601"
},
"dayOfMonth":7,
"dayOfWeek":"TUESDAY",
"dayOfYear":189,
"era":"CE",
"monthValue":7,
"leapYear":true
}
LocalDate同样有自己的字段,也被提到了LocalDateTime中。虽然多了一些东西,但是只把LocalDateTime中也出现的进行整理就行,对LocalTime做同样的工作。
枚举值转化
经过这些操作后形式如下
{
"offset": {
"totalSeconds": 25200,
"rules": {
"fixedOffset": true,
"transitionRules": [
],
"transitions": [
],
"class": "java.time.zone.ZoneRules"
},
"id": "+07:00",
"class": "java.time.ZoneOffset"
},
"dateTime": {
"date": {
"year": 2019,
"dayOfYear": 156,
"chronology": {
"calendarType": "iso8601",
"id": "ISO",
"class": "java.time.chrono.IsoChronology"
},
"dayOfWeek": "WEDNESSDAY",
"month": "MAY",
"dayOfMonth": 5,
"monthValue": 6
},
"time": {
"hour": 0,
"minute": 0,
"second": 0,
"nano": 841000000
},
"class": "java.time.LocalDateTime"
},
"zone": {
"rules": {
"fixedOffset": true,
"transitionRules": [
],
"transitions": [
],
"class": "java.time.zone.ZoneRules"
},
"id": "GMT+07:00",
"class": "java.time.ZoneRegion"
},
"class": "java.time.ZonedDateTime"
}
本以为问题结束了,结果又出现了新问题,月份的MAY无法被识别。原因显而易见,上面的源代码中可以看到月份本来应该是数值类型的枚举的,结果在json中变成了字符串,因此将"MAY"改为5即可,其他月份同理。
最终结果
{
"offset": {
"totalSeconds": 25200,
"rules": {
"fixedOffset": true,
"transitionRules": [
],
"transitions": [
],
"class": "java.time.zone.ZoneRules"
},
"id": "+07:00",
"class": "java.time.ZoneOffset"
},
"dateTime": {
"date": {
"year": 2019,
"dayOfYear": 156,
"chronology": {
"calendarType": "iso8601",
"id": "ISO",
"class": "java.time.chrono.IsoChronology"
},
"dayOfWeek": "WEDNESSDAY",
"month": 6,
"dayOfMonth": 5,
"monthValue": 6
},
"time": {
"hour": 0,
"minute": 0,
"second": 0,
"nano": 841000000
},
"class": "java.time.LocalDateTime"
},
"zone": {
"rules": {
"fixedOffset": true,
"transitionRules": [
],
"transitions": [
],
"class": "java.time.zone.ZoneRules"
},
"id": "GMT+07:00",
"class": "java.time.ZoneRegion"
},
"class": "java.time.ZonedDateTime"
}
至此ZonedDateTime的json终于可以被Jackson识别并加载对象了。