你遇到下面这个异常吗??
com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.util.Date` from String "2021-01-23 22:02:17": not a valid representation (error: Failed to parse Date value '2021-01-23 22:02:17': Cannot parse date "2021-01-23 22:02:17": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSSX', parsing fails (leniency? null))
这个异常是jackson在处理json字符串
to Object
类型的时候,json字符串
中包含类似"yyyy-MM-dd HH:mm:ss"
或"yyyy-MM-dd HH:mm"
格式的时间字符串,这种格式是属于国内常用时间格式,jackson默认是不认识的。当你的字符串中一旦出现这种,jackson会直接给你报错。
好在jackson可以让用户能灵活的配置。那么接下来,我们就去探索解决方案。
看起来最简单的解决方案
我们遇到的格式问题,jackson开发者必然也能想到,所以它为我们提供了@JsonFormat
注解,我们可以根据需要在属性上设置格式的转化,使用如下:
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date date;
pattern
指定序列化格式,timezone
指定时区;- 这种方式满足我们解决异常的需求,但是如果有太多的日期类需要转化,这真是让人头大,不过别怕,且看下文处理。
最省心的解决方案
JSON序列化和反序列化工具类
/**
* json工具类
*
* @author : uu
* @version : v1.0
* @Date 2021/1/23 21:34
*/
public class JSON {
private final static ObjectMapper OBJECT_MAPPER = new ObjectMapper();
static {
// 设置全局的TimeZone
OBJECT_MAPPER.setTimeZone(TimeZone.getTimeZone("GMT+8"));
// 全局处理时间 ,SimpleDateFormat的构造函数参数中是序列化需要滴!
OBJECT_MAPPER.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"){
// 这个方法是反序列化需要滴!
@Override
public Date parse(String source) throws ParseException {
return DateUtils.str2Data(source);
}
});
}
/**
* 字符串转化成对象
* @param json
* @param <T>
* @return
*/
public static <T> T parser(String json, Class<T> clazz) throws JsonProcessingException {
return OBJECT_MAPPER.readValue(json, clazz);
}
/**
* 对象转化成字符串
* @param object
* @return
*/
public static String stringify(Object object) throws JsonProcessingException {
return OBJECT_MAPPER.writeValueAsString(object);
}
}
时间工具类
/**
* 时间格式类
*
* @author : uu
* @version : v1.0
* @Date 2021/1/23 23:39
*/
public class DateUtils {
private static List<String> fmtList = new ArrayList<String>() {{
add("yyyy-MM-dd HH:mm:ss");
add("yyyy-MM-dd HH:mm");
add("yyyy-MM-dd HH");
add("yyyy-MM-dd");
}};
/***
* 转换字符串为日期date
*
* 自动匹配转化,支持fmtList中的几种格式
* @param dateStr
* @param fmtIndex
* @return
*/
public static Date str2Data(String dateStr, int... fmtIndex) {
int index = 0;
if (fmtIndex != null && fmtIndex.length > 0) {
index = fmtIndex[0];
}
if (index > fmtList.size() - 1) {
return null;
}
SimpleDateFormat format = new SimpleDateFormat(fmtList.get(index));
try {
Date date = format.parse(dateStr);
return date;
} catch (ParseException e1) {
return str2Data(dateStr, ++index);
}
}
/***
* date格式化字符串
* @param date
* @param fmt
* @return
*/
public static String date2str(Date date, String fmt) {
SimpleDateFormat format = new SimpleDateFormat(fmt);
return format.format(date);
}
}
注意:属性上的注解配置优先级高于全局配置,可以根据自身需求进行搭配使用。
其他解决方案
网上搜索序列化问题,很多文章告诉你实现jackson提供的序列化和反序列化接口,代码如下:
/**
* 自定义反序列化
*
* @author : uu
* @version : v1.0
* @Date 2021/1/23 23:39
*/
public class CustomDateDeserializer extends JsonDeserializer<Date> {
@Override
public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
return DateUtils.str2Data(jsonParser.getText());
}
}
/**
* 自定义序列化
* @author : uu
* @version : v1.0
* @Date 2021/1/24 00:04
*/
public class CustomDateSerializer extends JsonSerializer<Date> {
@Override
public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString(DateUtils.date2str(date, "yyyy-MM-dd HH:mm:ss"));
}
}
使用方式,只需要在需要处理的时间属性上增加序列化和反序列化注解,如下代码:
/**
* 自定义序列化
*/
@JsonDeserialize(using = CustomDateDeserializer.class)
@JsonSerialize(using = CustomDateSerializer.class)
private Date goHomeTime;
这种实现其实和
@JsonFormat
处理方式差不多,都需要在每个字段上设置。
总结一下喽
- 源码地址:jackson时间序列化问题
- jackson属性上的注解配置优先级高于全局配置。