Java前后端任意參數(shù)類型轉換方式(Date、LocalDateTime、BigDecimal)
1、前言
在前后端進行數(shù)據(jù)交互時,對于日期往往是通過時間戳進行交互,或者是Doule、BigDecimal等格式進行格式化保留固定小數(shù)點。
比如:Double與String、BigDecimal與String、Long與Date、Long與LocalDateTime
主要通過JsonSerializer
與JsonDeserializer
進行實現(xiàn):
- JsonSerializer:用于后端數(shù)據(jù)返回前端時的序列化轉換;
- JsonDeserializer:用于前端數(shù)據(jù)請求后端時的反序列化轉換;
- 只有請求或者響應的實體中存定義好的轉換的類型才會進入自定義轉換器中,比如:轉換的字段類型為BigDecimal,如果請求/響應的實體中不包含BigDecimal類型,那么就不會進行到轉換器中
在SpringBoot
中可以通過配置實現(xiàn)全局參數(shù)的字段轉換,或者使用注解標注進行指定字段的轉換生效或者轉換失效;
2、配置類型全局轉換器
代碼以實現(xiàn)后端格式化BigDecimal數(shù)據(jù)和前后端交互LocalDateTime轉Long兩種形式進行演示,實際中可以根據(jù)情況進行修改轉換方式,實現(xiàn)任意類型的序列化轉換;
2.1、定義類型全局轉換器
該類中的反序列,對Get
請求無效,會出現(xiàn)類型轉換失敗的情況,對于Get
請求需要單獨根據(jù)定義反序列化轉換器;
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請求的數(shù)據(jù)轉換 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; } /** * 序列化實現(xiàn)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); } } } /** * 序列化實現(xiàn) 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); } } } /** * 反序列化實現(xiàn) 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
請求無效,會出現(xiàn)類型轉換失敗的情況,對于Get
請求需要單獨根據(jù)定義反序列化轉換器,比如:此處用到的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請求參數(shù)", notes = "測試POST請求參數(shù)") @ApiOperationSupport(order = 5) @GetMapping("/testGet") public Object testGet(Product vo) { System.out.println("請求參數(shù)時間:" + 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請求參數(shù)", notes = "測試POST請求參數(shù)") @ApiOperationSupport(order = 10) @PostMapping("/testPost") public Product testPost(@RequestBody Product vo) { System.out.println("請求參數(shù)時間:" + 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
參數(shù),傳入long
類型的時間戳
結果:
雖然前端傳入時參數(shù)為long類型
的時間戳,但是后端打印出的數(shù)據(jù)格式為LocalDateTime
,表示反序列化是轉換成功;
2、序列化測試
響應參數(shù)定義如下:
前端結果:
可以看出,在后端定義的實體中createTime
字段的類型為LocalDateTime
,price
字段的值是12.1
只有一位小數(shù);
當響應到前端時createTime
字段的值時Long
類型的時間戳,price
字段的值為12.10
并且是保留兩位小數(shù)的String
類型值。
3、WebMvcConfigurationSupport踩坑說明
如果項目中存在繼承WebMvcConfigurationSupport
的配置類,那么在定義DataConverterConfig(全局轉換器)
時的配置就需要做出調(diào)整,調(diào)整如下:
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; } /** * 序列化實現(xiàn)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); } } } /** * 序列化實現(xiàn) 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); } } } /** * 反序列化實現(xiàn) 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); } }
總結
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Java自帶的Http?Server實現(xiàn)設置返回值的類型(content-type)
這篇文章主要介紹了Java自帶的Http?Server實現(xiàn)設置返回值的類型(content-type),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-11-11springboot1.X和2.X中如何解決Bean名字相同時覆蓋
這篇文章主要介紹了springboot1.X和2.X中如何解決Bean名字相同時覆蓋,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03運行SpringBoot項目請求響應流程分析以及404和500報錯的解決辦法
這篇文章主要介紹了運行Spring Boot項目請求響應流程分析以及404和500報錯的解決辦法,文中通過代碼示例和圖文講解的非常詳細,對大家的學習或工作有一定的幫助,需要的朋友可以參考下2024-12-12基于Gradle搭建Spring?5.3.13-release源碼閱讀環(huán)境的詳細流程
這篇文章主要介紹了基于Gradle搭建Spring?5.3.13-release源碼閱讀環(huán)境,首先安裝jdk、gradle等一系列必要操作,本文通過實例代碼相結合給大家講解的非常詳細,需要的朋友可以參考下2022-04-04