java:支持多种日期格式的Jackson自适应日期类型解析器实现

22 篇文章 1 订阅
本文介绍了如何在Jackson库中实现一个自适应日期解析器,通过继承`StdDeserializer`并重写`parseDate`方法,允许处理多种日期格式。作者还展示了如何创建特定类型的日期解析器,如`CalendarDeserializer`、`DateDeserializer`等。
摘要由CSDN通过智能技术生成

上一篇博客《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

  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

10km

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值