FastJson配置全局LocalDateTime序列化

最近工作中使用到了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<String, LocalDateTime>,网上也有很多,这里就提供一下代码

@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完败,有可能是我学艺不精,毕竟我是菜鸡。

  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
fastjson2 中,可以通过实现 SerializerFeature 和 Feature 接口来实现全局枚举的序列化和反序列化。 1. 全局枚举的序列化 可以通过实现 SerializerFeature 接口来实现全局枚举的序列化,例如: ```java public enum MyEnum { A, B, C; } public class MyEnumSerializer implements ContextValueSerializer, SerializerFeature { @Override public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException { if (object == null) { serializer.getWriter().writeNull(); return; } Enum<?> e = (Enum<?>) object; serializer.getWriter().writeValue(e.ordinal()); } @Override public boolean writeAsArray() { return false; } @Override public boolean writeNull() { return true; } @Override public boolean writeEnumUsingName() { return false; } @Override public boolean writeEnumUsingToString() { return false; } @Override public boolean writeEnumUsingOrdinal() { return true; } @Override public boolean writeSlashAsSpecial() { return false; } @Override public boolean writeTabAsSpecial() { return false; } @Override public boolean writeQuoteCharAsSpecial() { return false; } @Override public boolean writeSingleQuoteAsChar() { return false; } @Override public boolean writeClassName() { return false; } @Override public boolean notWriteDefaultValue() { return false; } @Override public boolean browserSecure() { return false; } @Override public boolean writeDateUseDateFormat() { return false; } @Override public boolean sortField() { return false; } @Override public boolean writeTab() { return false; } @Override public boolean prettyFormat() { return false; } @Override public boolean writeContentLength() { return false; } @Override public boolean quoteFieldNames() { return false; } @Override public boolean useSingleQuotes() { return false; } @Override public boolean writeNonStringValueAsString() { return false; } @Override public boolean notWriteRootClassName() { return false; } @Override public boolean beanToArray() { return false; } @Override public boolean writeBigDecimalAsPlain() { return false; } @Override public boolean ignoreNonFieldGetter() { return false; } @Override public boolean writeEnumUsingToStringUsingGlobalSetting() { return false; } @Override public boolean writeEnumUsingNameUsingGlobalSetting() { return false; } @Override public boolean writeDirect() { return false; } @Override public boolean browserCompatible() { return false; } } ``` 然后,在初始化 JSON 序列化器时,可以将该序列化器添加到全局的 SerializerFeature 中,例如: ```java SerializeConfig config = new SerializeConfig(); config.put(MyEnum.class, MyEnumSerializer.INSTANCE); JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.config(JSON.DEFAULT_GENERATE_FEATURE, MyEnum.class); ``` 这样,无论什么时候序列化 MyEnum 类型的枚举值,都会使用 MyEnumSerializer 中定义的方式进行序列化。 2. 全局枚举的反序列化 可以通过实现 Feature 接口来实现全局枚举的反序列化,例如: ```java public enum MyEnum { A, B, C; } public class MyEnumFeature implements Feature { @Override public int getMask() { return 0; } @Override public boolean isEnabled(int features) { return true; } @Override public boolean isEnabled(int features, int feature) { return true; } @Override public int config(int features, int feature, boolean state) { if (feature == MyEnumFeature.WRITE_ENUM_USING_ORDINAL.mask) { return state ? features | MyEnumFeature.WRITE_ENUM_USING_ORDINAL.mask : features & ~MyEnumFeature.WRITE_ENUM_USING_ORDINAL.mask; } return features; } public static final MyEnumFeature WRITE_ENUM_USING_ORDINAL = new MyEnumFeature(1); public static final MyEnumFeature WRITE_ENUM_USING_NAME = new MyEnumFeature(2); private final int mask; private MyEnumFeature(int mask) { this.mask = mask; } } ``` 然后,在初始化 JSON 解析器时,可以将该 Feature 添加到全局的 Feature 中,例如: ```java ParserConfig config = new ParserConfig(); config.addCompatibleFeature(MyEnumFeature.WRITE_ENUM_USING_ORDINAL); JSON.DEFAULT_PARSER_FEATURE |= MyEnumFeature.WRITE_ENUM_USING_ORDINAL.mask; ``` 这样,无论什么时候反序列化 MyEnum 类型的枚举值,都会使用 MyEnumFeature 中定义的方式进行反序列化

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值