最近工作中使用到了JDK1.8 time包,逐步取代以前的Date类,相比以前,time包的API的确好用多了,相关内容在网上有很多,就不累赘了。
但是有点蛋疼的地方在于LocalDateTime支持ISO8601标准,即[2018-10-10T05:51:31.924Z]。在MVC中,习惯性用[yyyy-MM-dd HH:mm:ss]。
好在Spring带有注解。如:
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss)
private LocalDateTime ldt;
@RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date
参数在Param上,可以使用@DateTimeFormat在参数或者参数实体的属性上。对于参数在body上,则可以通过自定义拓展Formatter或者Converter
@Configuration
public class LocalDateTimeFormatConfig {
@Bean
public Formatter<LocalDate> localDateFormatter() {
return new Formatter<LocalDate>() {
@Override
public @Nullable String print(@Nullable LocalDate object, @Nullable Locale locale) {
if (Objects.isNull(object)){
return null;
}
return object.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
}
@Override
public @Nullable LocalDate parse(@Nullable String text, @Nullable Locale locale) {
if (!StringUtils.hasText(text)) {
return null;
}
return LocalDate.parse(text, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
}
};
}
@Bean
public Formatter<LocalDateTime> localDateTimeFormatter() {
return new Formatter<LocalDateTime>() {
@Override
public @Nullable String print(@Nullable LocalDateTime object, @Nullable Locale locale) {
if (Objects.isNull(object)){
return null;
}
return object.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
@Override
public @Nullable LocalDateTime parse(@Nullable String text, @Nullable Locale locale) {
if (!StringUtils.hasText(text)) {
return null;
}
return LocalDateTime.parse(text, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
};
}
@Bean
public Formatter<LocalTime> localTimeFormatter() {
return new Formatter<LocalTime>() {
@Override
public @Nullable String print(@Nullable LocalTime object, @Nullable Locale locale) {
if (Objects.isNull(object)){
return null;
}
return object.format(DateTimeFormatter.ofPattern("HH:mm:ss"));
}
@Override
public @Nullable LocalTime parse(@Nullable String text, @Nullable Locale locale) {
if (!StringUtils.hasText(text)) {
return null;
}
return LocalTime.parse(text, DateTimeFormatter.ofPattern("HH:mm:ss"));
}
};
}
}
public class LocalDateTimeConvertConfig {
private String dateTimePattern = "yyyy-MM-dd HH:mm:ss";
private String datePattern = "yyyy-MM-dd";
@Bean
public Converter<String, LocalDateTime> LocalDateTimeConvert() {
return source -> {
DateTimeFormatter df = DateTimeFormatter.ofPattern(dateTimePattern);
LocalDateTime date = null;
try {
date = LocalDateTime.parse(source, df);
} catch (Exception e) {
e.printStackTrace();
}
return date;
};
}
@Bean
public Converter<String, LocalDate> localDateConverter() {
return source -> {
DateTimeFormatter df = DateTimeFormatter.ofPattern(datePattern);
LocalDate date = null;
try {
date = LocalDate.parse(source,df);
}catch (Exception e){
e.printStackTrace();
}
return date;
};
}
// 这里是使用JackSon全局序列化LocalDateTime,而我使用是FastJso,蛋疼
@Bean
public LocalDateTimeSerializer localDateTimeDeserializer() {
return new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(dateTimePattern));
}
@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
return builder -> builder.serializerByType(LocalDateTime.class, localDateTimeDeserializer());
}
}
这里要说的问题就是现在工作项目中遇到的问题,使用的FastJson,而不是MVC默认的JackSon,在序列化LocalDateTime输出到前端时候,是[2018-10-10T05:51:31.924Z],而想要输出的是yyyy-MM-dd HH:mm:ss格式,至此我们需要定制LocalDateTime类的序列化。我们一样可以使用注解解决,使用FastJson的@JSONField(format = “yyyy-MM-dd HH:mm:ss”),在需要特殊序列化的成员属性上。
为了减少注解的使用,尝试是否可以让FastJson默认根据我们的规则去序列化LocalDateTime类。
首先祭出常规的FastJsonConfig:
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(
SerializerFeature.PrettyFormat,
SerializerFeature.WriteMapNullValue,
...
SerializerFeature.WriteNullStringAsEmpty);
fastJsonConfig.setCharset(Charset.forName("utf-8"));
...
使用ValueFilter:
ValueFilter valueFilter = (o, s, o1) -> {
if (null == o1) {
o1 = "";
}
if (o1 instanceof LocalDateTime){
o1 = ((LocalDateTime) o1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
return o1;
};
fastJsonConfig.setSerializeFilters(valueFilter);
// 缺点,这是过滤级别的序列化,序列化的结果取决于o1的值,如将LocalDateTime类型的o1转换为String类型的o1,后续将不会调用LocalDateTime的序列化器,因为已经不是LocalDateTime类型了
指定fastJsonConfig的DateFormat:
fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");
...
// 缺点,指定后,将不会使用@JSONField注解上的format属性,包括并不限于Date类,LocalDateTime类,LocalDate类。(慎用)
自定义LocalDateTimeSerializer实现ObjectSerializer接口,重写write方法:
public class LocalDateTimeSerializer implements ObjectSerializer {
public static final LocalDateTimeSerializer instance = new LocalDateTimeSerializer();
private static final String defaultPattern = "yyyy-MM-dd HH:mm:ss";
public LocalDateTimeSerializer() {
}
@Override
public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
SerializeWriter out = serializer.out;
if (object == null) {
out.writeNull();
} else {
LocalDateTime result = (LocalDateTime) object;
out.writeString(result.format(DateTimeFormatter.ofPattern(defaultPattern)));
}
}
}
SerializeConfig serializeConfig = SerializeConfig.globalInstance;
serializeConfig.put(LocalDateTime.class, LocalDateTimeSerializer.instance);
fastJsonConfig.setSerializeConfig(serializeConfig);
// 缺点,@JSONField注解一样会失效,仅限于LocalDateTime类
...
如果使用的是JackSon,只需要在实例化LocalDateTimeSerializer的时候,指定一个DateTimeFormatter即可,方便很多,FastJson完败,有可能是我学艺不精,毕竟我是菜鸡。