SpringBoot中实体类Json格式序列化问题
一、引言
在使用SpringBoot开发项目时,通常需要将数据以JSON格式传递到前端。如果数据库表的主键类型是BigInt,对应Java实体类的主键类型为Long,且主键值是通过雪花算法生成的,可能会在前端展示时出现精度丢失问题。这是因为Java的Long类型在前端的JavaScript中会被解析为Number类型,而Number类型精度最高为15位,超过这个长度时会导致精度丢失。
此外,诸如LocalDateTime、LocalDate、LocalTime等表示时间的类,在不进行任何配置直接进行JSON格式的序列化时,会转换成对应的标准时间表示的字符串,对于通过前端进行操作的用户而言这样的表示方式并不友好,因此也需要对上述描述时间的类进行合理的序列化格式配置。
二、方案一
1.自定义ObjectMapper子类
自定义JacksonObjectMapper类继承ObjectMapper类,注册Long、BigInteger类型的序列化器,将其序列化为String类型,同时为LocalDateTime、LocalDate、LocalTime分别注册序列化和反序列化器,分别使用常用时间表示格式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class JacksonObjectMapper extends ObjectMapper { public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd"; public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
public JacksonObjectMapper() { super(); this.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
SimpleModule simpleModule = new SimpleModule() .addSerializer(BigInteger.class, ToStringSerializer.instance) .addSerializer(Long.class, ToStringSerializer.instance) .addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT))) .addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT))) .addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT))) .addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT))) .addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT))) .addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
this.registerModule(simpleModule); } }
|
2.配置项目全局生效
在SpringBoot项目的配置类中注册自定义的JacksonObjectMapper,新建消息转换器实例对象,将JacksonObjectMapper实例化添加到消息转换器实例中,最后将消息转换器示例添加到列表首位以优先实现该配置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| @Configuration public class WebMvcConfig extends WebMvcConfigurationSupport {
@Override protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) { MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
messageConverter.setObjectMapper(new JacksonObjectMapper());
converters.addFirst(messageConverter); } }
|
三、方案二
以Employee实体类为例,其中Long类型的id为主键,对其加上@JsonFormat(shape = JsonFormat.Shape.STRING)注解,可使得实例化对象序列化为JSON格式时,Long类型自动转换为String类型。对于类型为LocalDateTime的createTime和updateTime,加上@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")注解可使得序列化过程中,自动转换为pattern格式的字符串。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| @Data public class Employee implements Serializable { @Serial private static final long serialVersionUID = 1L;
@JsonFormat(shape = JsonFormat.Shape.STRING) private Long id;
private String username;
private String name;
private String password;
private String phone;
private String sex;
private String idNumber;
private Integer status;
@TableField(fill = FieldFill.INSERT) @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE) @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime updateTime;
@TableField(fill = FieldFill.INSERT) private Long createUser;
@TableField(fill = FieldFill.INSERT_UPDATE) private Long updateUser; }
|
四、写在最后
采用全局配置能够简单高效的将格式化规则应用于整个项目,但需要书写本身不熟悉的配置类,容易因API的语法调用错误导致大量的时间浪费。使用注解的方式可以灵活的处理需要进行配置的成员变量,但当实体类数量庞大时,需要频繁添加同样的注解,造成大量重复性的工作,可以考虑将通用的属性例如id、createTime、updateTime抽象到基类中,所有实体类均继承该基类。