封装配置类
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.TemporalAccessor;
import java.util.Objects;
import java.util.regex.Pattern;
/**
* 基于Jackson对象映射器生成器定制器
*/
@Configuration
public class JacksonObjectMapperCustomizer implements Jackson2ObjectMapperBuilderCustomizer {
public static final DateTimeFormatter YEAR_MONTH = DateTimeFormatter.ofPattern("yyyy-MM");
public static final DateTimeFormatter LOCAL_DATE = DateTimeFormatter.ofPattern("yyyy-MM-dd");
public static final DateTimeFormatter HOUR_MINUTE = DateTimeFormatter.ofPattern("HH:mm");
public static final DateTimeFormatter LOCAL_TIME = DateTimeFormatter.ofPattern("HH:mm:ss");
public static final DateTimeFormatter LOCAL_DATE_TIME = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public static final Pattern yyyy_MM = Pattern.compile("\\d{4}-\\d{2}");
public static final Pattern yyyy_MM_dd = Pattern.compile("\\d{4}-\\d{2}-\\d{2}");
public static final Pattern HH_mm = Pattern.compile("([0-1]?[0-9]|2[0-3]):([0-5][0-9])");
public static final Pattern HH_mm_ss = Pattern.compile("([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])");
public static final Pattern yyyy_MM_dd_HH_mm_ss = Pattern.compile("\\d{4}-\\d{2}-\\d{2}([ ])([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])");
@Override
public void customize(Jackson2ObjectMapperBuilder builder) {
// LocalDateTime序列化、反序列化
builder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(LOCAL_DATE_TIME));
builder.deserializerByType(LocalDateTime.class, new FlexibleLocalDateTimeDeserializer(LOCAL_DATE_TIME));
// LocalDate序列化、反序列化
builder.serializerByType(LocalDate.class, new LocalDateSerializer(LOCAL_DATE));
builder.deserializerByType(LocalDate.class, new FlexibleLocalDateDeserializer(LOCAL_DATE));
// LocalTime序列化、反序列化
builder.serializerByType(LocalTime.class, new LocalTimeSerializer(LOCAL_TIME));
builder.deserializerByType(LocalTime.class, new FlexibleLocalTimeDeserializer(LOCAL_TIME));
// Long序列化(序列化成字符串,因为精度问题,前端拿到数字类型会丢失精度)
builder.serializerByType(Long.class, ToStringSerializer.instance);
}
/**
* 灵活的LocalDateTime反序列化
* 支持格式: yyyy-MM-dd HH:mm:ss, yyyy-MM-dd, yyyy-MM
*/
public static class FlexibleLocalDateTimeDeserializer extends LocalDateTimeDeserializer {
private FlexibleLocalDateTimeDeserializer() {
this(LOCAL_DATE_TIME);
}
public FlexibleLocalDateTimeDeserializer(DateTimeFormatter formatter) {
super(formatter);
}
@Override
public LocalDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException, JsonProcessingException {
String timeStr = parser.getText();
if (timeStr == null || (timeStr = timeStr.trim()).isEmpty()) return null;
else if (yyyy_MM_dd_HH_mm_ss.matcher(timeStr).matches())
return super.deserialize(parser, context);
else if (yyyy_MM_dd.matcher(timeStr).matches())
return parser.getCurrentName().toLowerCase().contains("end") ? LocalDate.parse(timeStr, LOCAL_DATE).atTime(LocalTime.MAX) : LocalDate.parse(timeStr, LOCAL_DATE).atStartOfDay();
else if (yyyy_MM.matcher(timeStr).matches())
return parser.getCurrentName().toLowerCase().contains("end") ? YearMonth.parse(timeStr, YEAR_MONTH).atEndOfMonth().atTime(LocalTime.MAX) : YearMonth.parse(timeStr, YEAR_MONTH).atDay(1).atStartOfDay();
else throw new DateTimeParseException("时间字符串【" + timeStr + "'】格式不正确", timeStr, 0);
}
}
/**
* 灵活的LocalDate反序列化
* 支持格式: yyyy-MM-dd HH:mm:ss, yyyy-MM-dd, yyyy-MM
*/
public static class FlexibleLocalDateDeserializer extends LocalDateDeserializer {
private FlexibleLocalDateDeserializer() {
this(LOCAL_DATE);
}
public FlexibleLocalDateDeserializer(DateTimeFormatter dtf) {
super(dtf);
}
@Override
public LocalDate deserialize(JsonParser parser, DeserializationContext context) throws IOException, JsonProcessingException {
String timeStr = parser.getText();
if (timeStr == null || (timeStr = timeStr.trim()).isEmpty()) return null;
else if (yyyy_MM_dd.matcher(timeStr).find())
return timeStr.length() > 10 ? LocalDateTime.parse(timeStr, LOCAL_DATE_TIME).toLocalDate() : LocalDate.parse(timeStr, LOCAL_DATE);
else if (yyyy_MM.matcher(timeStr).matches())
return parser.getCurrentName().toLowerCase().contains("end") ? YearMonth.parse(timeStr, YEAR_MONTH).atEndOfMonth() : YearMonth.parse(timeStr, YEAR_MONTH).atDay(1);
else throw new DateTimeParseException("时间字符串【" + timeStr + "'】格式不正确", timeStr, 0);
}
}
/**
* 灵活的LocalTime反序列化
* 支持格式: HH:mm:ss, HH:mm
*/
public static class FlexibleLocalTimeDeserializer extends LocalTimeDeserializer {
private FlexibleLocalTimeDeserializer() {
this(LOCAL_TIME);
}
public FlexibleLocalTimeDeserializer(DateTimeFormatter formatter) {
super(formatter);
}
@Override
public LocalTime deserialize(JsonParser parser, DeserializationContext context) throws IOException {
String timeStr = parser.getText();
if (timeStr == null || (timeStr = timeStr.trim()).isEmpty()) return null;
else if (HH_mm_ss.matcher(timeStr).matches()) return super.deserialize(parser, context);
else if (HH_mm.matcher(timeStr).matches()) return LocalTime.parse(timeStr, HOUR_MINUTE);
else throw new DateTimeParseException("时间字符串【" + timeStr + "'】格式不正确", timeStr, 0);
}
}
/**
* 年月日序列化
* 支持类型: LocalDateTime, LocalDate
*/
public static class LocalDateFormatSerializer extends JsonSerializer<TemporalAccessor> {
@Override
public void serialize(TemporalAccessor temporalAccessor, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
if (Objects.isNull(temporalAccessor)) return;
jsonGenerator.writeString(LOCAL_DATE.format(temporalAccessor));
}
}
/**
* 年月序列化
* 支持类型: LocalDateTime, LocalDate, YearMonth
*/
public static class YearMonthFormatSerializer extends JsonSerializer<TemporalAccessor> {
@Override
public void serialize(TemporalAccessor temporalAccessor, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
if (Objects.isNull(temporalAccessor)) return;
jsonGenerator.writeString(YEAR_MONTH.format(temporalAccessor));
}
}
/**
* 时分序列化
* 支持类型: LocalDateTime, LocalTime
*/
public static class HourMinuteFormatSerializer extends JsonSerializer<TemporalAccessor> {
@Override
public void serialize(TemporalAccessor temporalAccessor, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
if (Objects.isNull(temporalAccessor)) return;
jsonGenerator.writeString(HOUR_MINUTE.format(temporalAccessor));
}
}
/**
* 时分秒序列化
* 支持类型: LocalDateTime, LocalTime
*/
public static class LocalTimeFormatSerializer extends JsonSerializer<TemporalAccessor> {
@Override
public void serialize(TemporalAccessor temporalAccessor, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
if (Objects.isNull(temporalAccessor)) return;
jsonGenerator.writeString(LOCAL_TIME.format(temporalAccessor));
}
}
}
灵活处理LocalDateTime类型
// 入参格式: yyyy-MM-dd HH:mm:ss, yyyy-MM-dd, yyyy-MM
@JsonDeserialize(using = JacksonObjectMapperCustomizer.FlexibleLocalDateTimeDeserializer.class)
// 出参格式(默认): yyyy-MM-dd HH:mm:ss
// 出参格式: yyyy-MM-dd
// @JsonSerialize(using = JacksonObjectMapperCustomizer.LocalDateFormatSerializer.class)
// 出参格式: yyyy-MM
// @JsonSerialize(using = JacksonObjectMapperCustomizer.YearMonthFormatSerializer.class)
private LocalDateTime startTime;
灵活处理LocalDate类型
// 入参格式: yyyy-MM-dd HH:mm:ss, yyyy-MM-dd, yyyy-MM
@JsonDeserialize(using = JacksonObjectMapperCustomizer.FlexibleLocalDateDeserializer.class)
// 出参格式(默认): yyyy-MM-dd
// 出参格式: yyyy-MM
// @JsonSerialize(using = JacksonObjectMapperCustomizer.YearMonthFormatSerializer.class)
private LocalDate startTime;
灵活处理LocalTime类型
// 入参格式:HH:mm:ss, HH:mm
@JsonDeserialize(using = JacksonObjectMapperCustomizer.FlexibleLocalTimeDeserializer.class)
// 出参格式(默认): HH:mm:ss
// 出参格式: HH:mm
@JsonSerialize(using = JacksonObjectMapperCustomizer.HourMinuteFormatSerializer.class)
private LocalTime startTime;
命名小细节
当变量名不包含end
字符串时, 默认填充起始时间, 反之填充最大时间,
小结
时间处理封装的并不算彻底, 但足够解决大部分需求, 封装不一定越多越好, 实用就行。
如不满足业务需求, 可自行扩展。
如有不完善的地方或错误, 请评论点出或联系我及时纠正, 谢谢。