Java前后端任意参数类型转换方式(Date、LocalDateTime、BigDecimal)
作者:一恍过去
1、前言
在前后端进行数据交互时,对于日期往往是通过时间戳进行交互,或者是Doule、BigDecimal等格式进行格式化保留固定小数点。
比如:Double与String、BigDecimal与String、Long与Date、Long与LocalDateTime
主要通过JsonSerializer
与JsonDeserializer
进行实现:
- JsonSerializer:用于后端数据返回前端时的序列化转换;
- JsonDeserializer:用于前端数据请求后端时的反序列化转换;
- 只有请求或者响应的实体中存定义好的转换的类型才会进入自定义转换器中,比如:转换的字段类型为BigDecimal,如果请求/响应的实体中不包含BigDecimal类型,那么就不会进行到转换器中
在SpringBoot
中可以通过配置实现全局参数的字段转换,或者使用注解标注进行指定字段的转换生效或者转换失效;
2、配置类型全局转换器
代码以实现后端格式化BigDecimal数据和前后端交互LocalDateTime转Long两种形式进行演示,实际中可以根据情况进行修改转换方式,实现任意类型的序列化转换;
2.1、定义类型全局转换器
该类中的反序列,对Get
请求无效,会出现类型转换失败的情况,对于Get
请求需要单独根据定义反序列化转换器;
DataConverterConfig:
import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.module.SimpleModule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.format.FormatterRegistry; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.io.IOException; import java.math.BigDecimal; import java.text.DecimalFormat; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.List; @Configuration public class DataConverterConfig { /** * 配置消息转换器 * * @return */ @Bean public WebMvcConfigurer webMvcConfigurer() { return new WebMvcConfigurer() { /** * 添加自定义消息转换器 */ @Override public void addFormatters(FormatterRegistry registry) { // 对于Get请求的数据转换 registry.addConverter(new TimeStampToLocalDateConverter()); } @Override public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); converter.setObjectMapper(serializingObjectMapper()); converters.add(0, converter); } }; } /** * Serializer配置 * * @return */ public ObjectMapper serializingObjectMapper() { ObjectMapper objectMapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); // 禁止null值字段进行序列化 // 如果有需要则进行使用 // objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // 添加默认序列化,将字段转化为转换String,支持各种可以直接使用toString()方法的类型 // 如果有需要则进行开启 // module.addSerializer(BigInteger.class, new ToStringSerializer()); // module.addSerializer(Long.class, new ToStringSerializer()); // module.addSerializer(Integer.class, new ToStringSerializer()); // module.addSerializer(BigInteger.class, new ToStringSerializer()); // 添加自定义序列化 Serializer module.addSerializer(BigDecimal.class, new BigDecimalSerializer()); module.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer()); // 添加自定义反序列化 Deserializer module.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer()); objectMapper.registerModule(module); return objectMapper; } /** * 序列化实现BigDecimal转化为String */ public static class BigDecimalSerializer extends JsonSerializer<BigDecimal> { @Override public void serialize(BigDecimal value, JsonGenerator gen, SerializerProvider serializers) throws IOException { String res = null; if (value != null) { int scale = value.scale(); if (scale > 2) { res = value.toString(); } else { DecimalFormat df = new DecimalFormat("#0.00"); res = df.format(value); } gen.writeString(res); } } } /** * 序列化实现 LocalDateTime转化为Long */ public static class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> { @Override public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException { if (value != null) { long timestamp = value.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); gen.writeNumber(timestamp); } } } /** * 反序列化实现 Long转化为为LocalDateTime(只对GET请求无效) */ public static class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> { @Override public LocalDateTime deserialize(JsonParser p, DeserializationContext deserializationContext) throws IOException { long timestamp = p.getValueAsLong(); if (timestamp > 0) { return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault()); } else { return null; } } } }
2.2、定义Get请求反序列化转换器
在DataConverterConfig
类中的定义的反序列化转换器,对Get
请求无效,会出现类型转换失败的情况,对于Get
请求需要单独根据定义反序列化转换器,比如:此处用到的Long转LocalDateTime反序列化转换,如果有其他类型就需要定义其他的反序列化转换器;
TimeStampToLocalDateConverter:
import org.springframework.core.convert.converter.Converter; import org.springframework.util.StringUtils; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; /** * 时间戳字符串转时间类型转换器 * */ public class TimeStampToLocalDateConverter implements Converter<String, LocalDateTime> { @Override public LocalDateTime convert(String text) { if (!StringUtils.isEmpty(text)) { long timestamp = Long.parseLong(text); if (timestamp > 0) { return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault()); } else { return null; } } return null; } }
2.3、定义实体
@Data public class Product { private Long id; private Integer num; private BigInteger count; private String name; private BigDecimal price; private BigDecimal realPrice; private LocalDateTime createTime; private Date time; }
2.4、测试Controller
@RestController @RequestMapping("/test") @Slf4j public class TestController { @ApiOperation(value = "测试GET请求参数", notes = "测试POST请求参数") @ApiOperationSupport(order = 5) @GetMapping("/testGet") public Object testGet(Product vo) { System.out.println("请求参数时间:" + vo.getCreateTime()); Product res = new Product(); res.setId(System.currentTimeMillis()); res.setNum(12); res.setCount(new BigInteger("10")); res.setName("测试名称"); res.setCreateTime(LocalDateTime.now()); res.setPrice(new BigDecimal("12.1")); res.setRealPrice(new BigDecimal("12.124")); res.setTime(new Date()); return res; } @ApiOperation(value = "测试POST请求参数", notes = "测试POST请求参数") @ApiOperationSupport(order = 10) @PostMapping("/testPost") public Product testPost(@RequestBody Product vo) { System.out.println("请求参数时间:" + vo.getCreateTime()); Product res = new Product(); res.setId(System.currentTimeMillis()); res.setNum(12); res.setCount(new BigInteger("10")); res.setName("测试名称"); res.setCreateTime(LocalDateTime.now()); res.setPrice(new BigDecimal("12.1")); res.setRealPrice(new BigDecimal("12.124")); return res; } }
2.5、测试结果
1、反序列化测试
请求时对于createTime
参数,传入long
类型的时间戳
结果:
虽然前端传入时参数为long类型
的时间戳,但是后端打印出的数据格式为LocalDateTime
,表示反序列化是转换成功;
2、序列化测试
响应参数定义如下:
前端结果:
可以看出,在后端定义的实体中createTime
字段的类型为LocalDateTime
,price
字段的值是12.1
只有一位小数;
当响应到前端时createTime
字段的值时Long
类型的时间戳,price
字段的值为12.10
并且是保留两位小数的String
类型值。
3、WebMvcConfigurationSupport踩坑说明
如果项目中存在继承WebMvcConfigurationSupport
的配置类,那么在定义DataConverterConfig(全局转换器)
时的配置就需要做出调整,调整如下:
1、DataConverterConfig修改处理
DataConverterConfig
中删除WebMvcConfigurer webMvcConfigurer()
方法,对ObjectMapper serializingObjectMapper()
方法加上@Bean
注解,更改后代码如下:
@Configuration public class DataConverterConfig { /** * Serializer配置 * * @return */ @Bean public ObjectMapper serializingObjectMapper() { ObjectMapper objectMapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); // 添加自定义序列化 Serializer module.addSerializer(BigDecimal.class, new BigDecimalSerializer()); module.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer()); // 添加自定义反序列化 Deserializer module.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer()); objectMapper.registerModule(module); return objectMapper; } /** * 序列化实现BigDecimal转化为String */ public static class BigDecimalSerializer extends JsonSerializer<BigDecimal> { @Override public void serialize(BigDecimal value, JsonGenerator gen, SerializerProvider serializers) throws IOException { String res = null; if (value != null) { int scale = value.scale(); if (scale > 2) { res = value.toString(); } else { DecimalFormat df = new DecimalFormat("#0.00"); res = df.format(value); } gen.writeString(res); } } } /** * 序列化实现 LocalDateTime转化为Long */ public static class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> { @Override public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException { if (value != null) { long timestamp = value.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); gen.writeNumber(timestamp); } } } /** * 反序列化实现 Long转化为为LocalDateTime(只对GET请求无效),对于GET请求需要单独订单转换器,比如:TimeStampToLocalDateConverter */ public static class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> { @Override public LocalDateTime deserialize(JsonParser p, DeserializationContext deserializationContext) throws IOException { long timestamp = p.getValueAsLong(); if (timestamp > 0) { return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault()); } else { return null; } } } }
2、WebMvcConfigurationSupport继承类中处理
WebMvcConfigurationSupport
继承类中新增addFormatters()
方法与extendMessageConverters
方法以及注入ObjectMapper
,更改后代码如下:
@Configuration public class WebMvcRegistrationsConfig extends WebMvcConfigurationSupport { @Resource private ObjectMapper serializingObjectMapper; /** * 添加自定义消息转换器 */ @Override protected void addFormatters(FormatterRegistry registry) { // 添加时间戳转日期类型消息转换器 registry.addConverter(new TimeStampToLocalDateConverter()); } @Override public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); converter.setObjectMapper(serializingObjectMapper); converters.add(0, converter); } }
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。