上一篇博客《java:支持更多日期格式的fastjson日期解析器(ObjectDeserializer)实现》介绍了可以自适应解析多种日期格式的Fastjson的日期解析器实现,本文说明如何在Jackson下实现同的样的自适应日期解析器。
jackson的标准解析器抽象类com.fasterxml.jackson.databind.deser.std.StdDeserializer
中已经提供了解析日期的实现方法(_parseDate
)。其中 _parseDate(JsonParser p, DeserializationContext ctxt)
方法已经提供了完善的TOKEN分析逻辑(如下),所以没必要从TOKEN识别开始做。只需要重写_parseDate(String value, DeserializationContext ctxt)
方法,对输入字符串尝试使用多种日期格式解析就可以了。
protected java.util.Date _parseDate(JsonParser p, DeserializationContext ctxt)
throws IOException
{
JsonToken t = p.getCurrentToken();
if (t == JsonToken.VALUE_NUMBER_INT) {
return new java.util.Date(p.getLongValue());
}
if (t == JsonToken.VALUE_NULL) {
return (java.util.Date) getNullValue(ctxt);
}
if (t == JsonToken.VALUE_STRING) {
return _parseDate(p.getText().trim(), ctxt);
}
// [databind#381]
if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
p.nextToken();
final Date parsed = _parseDate(p, ctxt);
t = p.nextToken();
if (t != JsonToken.END_ARRAY) {
handleMissingEndArrayForSingle(p, ctxt);
}
return parsed;
}
return (java.util.Date) ctxt.handleUnexpectedToken(_valueClass, p);
}
BaseDateDeserializer
所以核心的实现代码并不多,如下:
import static net.gdface.utils.DateSupport.castToDate;
import static net.gdface.utils.DateSupport.parseAsDate;
import static com.google.common.base.Preconditions.checkArgument;
import java.io.IOException;
import java.util.Calendar;
import java.util.Date;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
/**
* 自适应日期解析实现类
* @author guyadong
*
* @param <T> 日期类型,要求为java.util.Date或 Calendar或其子类
*/
public static class BaseDateDeserializer<T> extends StdDeserializer<T> {
public BaseDateDeserializer(Class<? extends T> valueClass) {
super(valueClass);
/** 参数类型检查:要求为java.util.Date或 Calendar或其子类 */
checkArgument(
null != valueClass
&& (Date.class.isAssignableFrom(valueClass) || Calendar.class.isAssignableFrom(valueClass)),
"valueClass required Date or Calendar");
}
/**
* 重写父类方法,先调用父类方法,调用失败时调用用 parseAsDate 自适应解析
*/
@Override
protected Date _parseDate(String value, DeserializationContext ctxt) throws IOException {
try {
return super._parseDate(value, ctxt);
} catch (Exception e) {
/** 尝试使用更多的日期格式解析字符串,解析失败抛出原异常 */
Date date = parseAsDate(value, Date.class);
if(date != null) {
return date;
}
throw e;
}
}
@SuppressWarnings("unchecked")
@Override
public T deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
return (T) castToDate(_parseDate(jp, ctxt),handledType());
}
}
DateSupport
上面的代码中调用的parseAsDate,castToDate
来自DateSupport
,它在PATTERNS数组中定义常用的日期格式。对于日期字符串会顺序尝试使用这些格式来解析直到能够成功解析为止。
代码参见上一篇博客《java:支持更多日期格式的fastjson日期解析器(ObjectDeserializer)实现》中《DateSupport》章节
JacksonDateDeserializers
有了BaseDateDeserializer实现,实现java.util.Date,java.sql.Date,java.sql.Timestamp,Calendar
的对应解析器就很简单了,如下代码实现了CalendarDeserializer,DateDeserializer,SqlDateDeserializer,TimestampDeserializer:
import static net.gdface.utils.DateSupport.castToDate;
import static net.gdface.utils.DateSupport.parseAsDate;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkArgument;
import java.io.IOException;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.sql.Timestamp;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
/**
* 自适应日期类型解析器实现,支持更多日期格式
* @author guyadong
* @see 2.8.14
*/
public class JacksonDateDeserializers {
@SuppressWarnings({ "unchecked", "rawtypes" })
public static <T>JsonDeserializer<T> find(Class<T> rawType)
{
JsonDeserializer deserializer = null;
if(null != rawType) {
if (rawType == Calendar.class) {
deserializer = CalendarDeserializer.INSTANCE;
}
if (rawType == java.util.Date.class) {
deserializer = DateDeserializer.INSTANCE;
}
if (rawType == java.sql.Date.class) {
deserializer = SqlDateDeserializer.INSTANCE;
}
if (rawType == Timestamp.class) {
deserializer = TimestampDeserializer.INSTANCE;
}
if (rawType == GregorianCalendar.class) {
deserializer = CalendarDeserializer.INSTANCE_GREGORIAN;
}
if (Date.class.isAssignableFrom(rawType) || Calendar.class.isAssignableFrom(rawType)) {
deserializer = new BaseDateDeserializer(rawType);
}
}
return deserializer;
}
/**
* 自适应日期解析实现类
* @author guyadong
*
* @param <T> 日期类型,要求为java.util.Date或 Calendar或其子类
*/
public static class BaseDateDeserializer<T> extends StdDeserializer<T> {
private static final long serialVersionUID = 3686273235838778015L;
public BaseDateDeserializer(Class<? extends T> valueClass) {
super(valueClass);
/** 参数类型检查:要求为java.util.Date或 Calendar或其子类 */
checkArgument(
null != valueClass
&& (Date.class.isAssignableFrom(valueClass) || Calendar.class.isAssignableFrom(valueClass)),
"valueClass required Data or Calendar");
}
/**
* 重写父类方法,先调用父类方法,调用失败时调用用 parseAsDate 自适应解析
*/
@Override
protected Date _parseDate(String value, DeserializationContext ctxt) throws IOException {
try {
return super._parseDate(value, ctxt);
} catch (Exception e) {
/** 尝试使用更多的日期格式解析字符串,解析失败抛出原异常 */
Date date = parseAsDate(value, Date.class);
if(date != null) {
return date;
}
throw e;
}
}
@SuppressWarnings("unchecked")
@Override
public T deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
return (T) castToDate(_parseDate(jp, ctxt),handledType());
}
}
public static class CalendarDeserializer extends BaseDateDeserializer<Calendar>{
public static final CalendarDeserializer INSTANCE = new CalendarDeserializer();
public static final CalendarDeserializer INSTANCE_GREGORIAN = new CalendarDeserializer(GregorianCalendar.class);
public CalendarDeserializer(Class<? extends Calendar> type) {
super(type);
}
public CalendarDeserializer() {
super(Calendar.class);
}
}
public static class DateDeserializer extends BaseDateDeserializer<Date>{
public static final DateDeserializer INSTANCE = new DateDeserializer();
public DateDeserializer() {
super(Date.class);
}
}
public static class SqlDateDeserializer extends BaseDateDeserializer<java.sql.Date>{
public static final SqlDateDeserializer INSTANCE = new SqlDateDeserializer();
public SqlDateDeserializer() {
super(java.sql.Date.class);
}
}
public static class TimestampDeserializer extends BaseDateDeserializer<Timestamp>{
public static final TimestampDeserializer INSTANCE = new TimestampDeserializer();
public TimestampDeserializer() {
super(Timestamp.class);
}
}
}
调用示例代码
@Test
public void test9JacksonDateDeserializer() {
try {
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule()
.addDeserializer(Date.class, JacksonDateDeserializers.findCached(Date.class))
.addDeserializer(java.sql.Date.class, JacksonDateDeserializers.SqlDateDeserializer.INSTANCE)
.addDeserializer(Timestamp.class, JacksonDateDeserializers.TimestampDeserializer.INSTANCE)
.addDeserializer(Calendar.class, JacksonDateDeserializers.CalendarDeserializer.INSTANCE)
.addDeserializer(GregorianCalendar.class, JacksonDateDeserializers.findCached(GregorianCalendar.class))
;
mapper.registerModule(module);
String input="\"Tuesday February 27 10:43:27 CST 2024\"";
{
Date parsed = mapper.readValue(input, Date.class);
log("{}",parsed);
assertTrue(parsed.toString().equals("Tue Feb 27 10:43:27 CST 2024"));
}
{
java.sql.Date parsed = mapper.readValue(input, java.sql.Date.class);
log("{}",parsed);
assertTrue(parsed.toString().equals("2024-02-27"));
}
{
Timestamp parsed = mapper.readValue(input, Timestamp.class);
log("{}",parsed);
assertTrue(parsed.toString().equals("2024-02-27 10:43:27.0"));
}
{
Calendar parsed = mapper.readValue(input, Calendar.class);
log("{}",parsed);
assertTrue(parsed.getTime().toString().equals("Tue Feb 27 10:43:27 CST 2024"));
}
{
GregorianCalendar parsed = mapper.readValue(input, GregorianCalendar.class);
log("{}",parsed);
assertTrue(parsed.getTime().toString().equals("Tue Feb 27 10:43:27 CST 2024"));
}
} catch (Throwable e) {
e.printStackTrace();
fail(e.getMessage());
}
}
以上实现的完整代码参见:
https://gitee.com/l0km/common-java/blob/master/common-base2/src/main/java/net/gdface/json/JacksonDateDeserializers.java
测试代码参见 :
https://gitee.com/l0km/common-java/blob/master/common-base2/src/test/java/net/gdface/json/JacksonTest.java