报错信息
msg: "服务端异常,请联系管理员
JSON parse error: Cannot deserialize value of type java.util.Date
from String "2023-11-13 13:13:35": not a valid representation (error: Failed to parse Date value '2023-11-13 13:13:35': Cannot parse date "2023-11-13 13:13:35": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSSX', parsing fails (leniency? null)); nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type java.util.Date
from String "2023-11-13 13:13:35": not a valid representation (error: Failed to parse Date value '2023-11-13 13:13:35': Cannot parse date "2023-11-13 13:13:35": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSSX', parsing fails (leniency? null))↵ at [Source: (PushbackInputStream); line: 1, column: 233] (through reference chain: com.java1234.entity.Product["hotDateTime"])"
配置文件配置application.yml:
bash
spring:
jackson:
#字符串时间,用这行表示
date-format: yyyy-MM-dd
#设置为东八区时间
timezone: GMT+8
bash
spring:
# 配置日期格式化
jackson:
date-format: yyyy-MM-dd HH:mm:ss #时间戳统一转换为指定格式
time-zone: GMT+8 # 时区修改为东8区
application.properties配置方式
bash
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss #时间戳统一转换为指定格式
spring.jackson.time-zone=GMT+8 # 时区修改为东8区
【注意】这里需要修改时区time-zone:数据库默认时区是格林尼治的时间,如果不设置,会比实际时间少8个小时(北京时间)。
项目场景:
前端画面上有一个可以选择年月的日期选择器,代码如下:
bash
<el-form-item label="统计月份" prop="billsDate" v-if="isShowMonth">
<el-date-picker clearable size="small"
v-model="queryParams.billsDate"
type="month"
value-format="yyyy-MM"
placeholder="请选择统计月份"
:disabled="inputDisable">
</el-date-picker>
</el-form-item>
选择的数据使用post请求将数据传递给后端
问题描述
后端接收的时候报如下错误
JSON parse error: Cannot deserialize value of type java.util.Date
from String "2022-01":
后端接收的实体类如下
bash
/**
* 客户ID
*/
private Long companyId;
/**
* 客户code
*/
private String companyCode;
/**
* 账单统计类型
*/
private Integer billStatisticsType;
/**
* 账单时间
*/
private Date billsDate;
原因分析:
因为传入的参数是 String 类型的,而用来接收参数的项目的属性是 java.util.Date 类型的,类型无法转换。
springboot默认采用jackson,而jackson只能识别以下几种日期格式
"yyyy-MM-dd'T'HH:mm:ss.SSSZ"; "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; "yyyy-MM-dd"; "EEE, dd MMM yyyy HH:mm:ss zzz"; long类型的时间戳(毫秒时间戳)
解决方案:
所以在接收到数据的时候,需要通过jackson把数据转化。jackson转化默认的时间格式 'yyyy-MM-dd'T'HH:mm:ss.SSS',所以就会出现异常,所以要设置时区的格式为"yyyy-MM"。
添加@JsonFormat注解,来说明接收格式
代码如下
bash
/**
* 客户ID
*/
private Long companyId;
/**
* 客户code
*/
private String companyCode;
/**
* 账单统计类型
*/
private Integer billStatisticsType;
/**
* 账单时间
*/
@JsonFormat(pattern = "yyyy-MM")
private Date billsDate;
SpringBoot中日期时间类型的序列化与反序列化
目录
1、背景
2、序列化时间戳
3、反序列化时间戳
4、使用示例
1、背景
在SpringBoot开发web
应用微服务时,常用Date类型表示日期时间,JDK8之后,可以使用Instant类型来表示时间戳,并持久化到数据库中。也有直接使用Long类型存储日期时间或时间戳的,但这种方式存储到数据库后,对于DBA
或运维查询数据库时,不太友好,没法直接看出来是什么日期或时间。大多数时候会使用Instant类型来表示时间戳。
在前后端分离的开发架构中,对于日期时间类型,后端接口在返回给前端时,需要将Instant类型转换为前端需要的时间戳即长整型Long(可序列化的类型),而前端一般也会提交时间戳类型给后端,后端需要将时间戳的长整型Long
转换为Instant类型。即需要实现对日期时间进行序列化和反序列化。SpringBoot框架中,实现对日期时间进行序列化和反序列化,只需要实现相应的接口即可完成。
序列化是将一个对象转换成字节序列的过程,可以将这些字节序列存储在磁盘或通过网络传输。反序列化是将字节序列转换回对象的过程。
2、序列化时间戳
将Instant类型转换为前端需要的长整型Long,可以通过继承JsonSerializer类,实现serialize方法即可。
bash
package com.fandou.component.json;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.time.Instant;
/**
* Instant序列化为Long类型
*/
public class InstantToLongSerializer extends JsonSerializer<Instant> {
@Override
public void serialize(Instant value, JsonGenerator generator, SerializerProvider serializers) throws IOException {
// 通过JSON生成器,将要序列化的Instant类型对象即value的时间戳值写入为数值,即前端需要的长整型
generator.writeNumber(value.toEpochMilli());
}
}
3、反序列化时间戳
同样,将长整型Long转换为Instant类型,可以通过继承JsonDeserializer类,实现deserialize方法即可。
bash
package com.fandou.component.json;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException;
import java.time.Instant;
/**
* Long反序列化为Instant类型
*
*/
public class LongToInstantDeserializer extends JsonDeserializer<Instant> {
@Override
public Instant deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
// 从JSON初始化器中读取对应的长整型时间错值,使用Instant的ofEpochMilli初始化为Instant类型返回
return Instant.ofEpochMilli(jsonParser.getValueAsLong());
}
}
4、使用示例
使用@JsonSerialize注解或@JsonDeserialize注解标记需要序列化或反序列化的对象属性上,注解中的using属性值指定为步骤2或步骤3的序列化类或反序列化类型即可。在运行的过程中,SpringBoot
框架会自动完成日期时间的序列化和反序列化。
bash
package com.example.demo.vo;
import com.fandou.component.json.InstantToLongSerializer;
import com.fandou.component.json.LongToInstantDeserializer;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import lombok.Getter;
import lombok.Setter;
import java.time.Instant;
/**
* 用户VO对象
*
*
*/
@Setter
@Getter
public class User {
/**
* 主键id
*/
private Long id;
/**
* 用户账号
*/
private String userAccount;
/**
* 用户昵称
*/
private String nickname;
/**
* 创建时间:使用@JsonSerialize注解和@JsonDeserialize注解实现创建时间属性的序列化和反序列化
*/
@JsonSerialize(using = InstantToLongSerializer.class)
@JsonDeserialize(using = LongToInstantDeserializer.class)
private Instant createTime;
}