Java前后端任意參數(shù)類(lèi)型轉(zhuǎn)換方式(Date、LocalDateTime、BigDecimal)
1、前言
在前后端進(jìn)行數(shù)據(jù)交互時(shí),對(duì)于日期往往是通過(guò)時(shí)間戳進(jìn)行交互,或者是Doule、BigDecimal等格式進(jìn)行格式化保留固定小數(shù)點(diǎn)。
比如:Double與String、BigDecimal與String、Long與Date、Long與LocalDateTime
主要通過(guò)JsonSerializer
與JsonDeserializer
進(jìn)行實(shí)現(xiàn):
- JsonSerializer:用于后端數(shù)據(jù)返回前端時(shí)的序列化轉(zhuǎn)換;
- JsonDeserializer:用于前端數(shù)據(jù)請(qǐng)求后端時(shí)的反序列化轉(zhuǎn)換;
- 只有請(qǐng)求或者響應(yīng)的實(shí)體中存定義好的轉(zhuǎn)換的類(lèi)型才會(huì)進(jìn)入自定義轉(zhuǎn)換器中,比如:轉(zhuǎn)換的字段類(lèi)型為BigDecimal,如果請(qǐng)求/響應(yīng)的實(shí)體中不包含BigDecimal類(lèi)型,那么就不會(huì)進(jìn)行到轉(zhuǎn)換器中
在SpringBoot
中可以通過(guò)配置實(shí)現(xiàn)全局參數(shù)的字段轉(zhuǎn)換,或者使用注解標(biāo)注進(jìn)行指定字段的轉(zhuǎn)換生效或者轉(zhuǎn)換失效;
2、配置類(lèi)型全局轉(zhuǎn)換器
代碼以實(shí)現(xiàn)后端格式化BigDecimal數(shù)據(jù)和前后端交互LocalDateTime轉(zhuǎn)Long兩種形式進(jìn)行演示,實(shí)際中可以根據(jù)情況進(jìn)行修改轉(zhuǎn)換方式,實(shí)現(xiàn)任意類(lèi)型的序列化轉(zhuǎn)換;
2.1、定義類(lèi)型全局轉(zhuǎn)換器
該類(lèi)中的反序列,對(duì)Get
請(qǐng)求無(wú)效,會(huì)出現(xiàn)類(lèi)型轉(zhuǎn)換失敗的情況,對(duì)于Get
請(qǐng)求需要單獨(dú)根據(jù)定義反序列化轉(zhuǎn)換器;
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 { /** * 配置消息轉(zhuǎn)換器 * * @return */ @Bean public WebMvcConfigurer webMvcConfigurer() { return new WebMvcConfigurer() { /** * 添加自定義消息轉(zhuǎn)換器 */ @Override public void addFormatters(FormatterRegistry registry) { // 對(duì)于Get請(qǐng)求的數(shù)據(jù)轉(zhuǎn)換 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值字段進(jìn)行序列化 // 如果有需要?jiǎng)t進(jìn)行使用 // objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // 添加默認(rèn)序列化,將字段轉(zhuǎn)化為轉(zhuǎn)換String,支持各種可以直接使用toString()方法的類(lèi)型 // 如果有需要?jiǎng)t進(jìn)行開(kāi)啟 // 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; } /** * 序列化實(shí)現(xiàn)BigDecimal轉(zhuǎn)化為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); } } } /** * 序列化實(shí)現(xiàn) LocalDateTime轉(zhuǎn)化為L(zhǎng)ong */ 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); } } } /** * 反序列化實(shí)現(xiàn) Long轉(zhuǎn)化為為L(zhǎng)ocalDateTime(只對(duì)GET請(qǐng)求無(wú)效) */ 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請(qǐng)求反序列化轉(zhuǎn)換器
在DataConverterConfig
類(lèi)中的定義的反序列化轉(zhuǎn)換器,對(duì)Get
請(qǐng)求無(wú)效,會(huì)出現(xiàn)類(lèi)型轉(zhuǎn)換失敗的情況,對(duì)于Get
請(qǐng)求需要單獨(dú)根據(jù)定義反序列化轉(zhuǎn)換器,比如:此處用到的Long轉(zhuǎn)LocalDateTime反序列化轉(zhuǎn)換,如果有其他類(lèi)型就需要定義其他的反序列化轉(zhuǎn)換器;
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; /** * 時(shí)間戳字符串轉(zhuǎn)時(shí)間類(lèi)型轉(zhuǎn)換器 * */ 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、定義實(shí)體
@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、測(cè)試Controller
@RestController @RequestMapping("/test") @Slf4j public class TestController { @ApiOperation(value = "測(cè)試GET請(qǐng)求參數(shù)", notes = "測(cè)試POST請(qǐng)求參數(shù)") @ApiOperationSupport(order = 5) @GetMapping("/testGet") public Object testGet(Product vo) { System.out.println("請(qǐng)求參數(shù)時(shí)間:" + vo.getCreateTime()); Product res = new Product(); res.setId(System.currentTimeMillis()); res.setNum(12); res.setCount(new BigInteger("10")); res.setName("測(cè)試名稱"); res.setCreateTime(LocalDateTime.now()); res.setPrice(new BigDecimal("12.1")); res.setRealPrice(new BigDecimal("12.124")); res.setTime(new Date()); return res; } @ApiOperation(value = "測(cè)試POST請(qǐng)求參數(shù)", notes = "測(cè)試POST請(qǐng)求參數(shù)") @ApiOperationSupport(order = 10) @PostMapping("/testPost") public Product testPost(@RequestBody Product vo) { System.out.println("請(qǐng)求參數(shù)時(shí)間:" + vo.getCreateTime()); Product res = new Product(); res.setId(System.currentTimeMillis()); res.setNum(12); res.setCount(new BigInteger("10")); res.setName("測(cè)試名稱"); res.setCreateTime(LocalDateTime.now()); res.setPrice(new BigDecimal("12.1")); res.setRealPrice(new BigDecimal("12.124")); return res; } }
2.5、測(cè)試結(jié)果
1、反序列化測(cè)試
請(qǐng)求時(shí)對(duì)于createTime
參數(shù),傳入long
類(lèi)型的時(shí)間戳
結(jié)果:
雖然前端傳入時(shí)參數(shù)為long類(lèi)型
的時(shí)間戳,但是后端打印出的數(shù)據(jù)格式為LocalDateTime
,表示反序列化是轉(zhuǎn)換成功;
2、序列化測(cè)試
響應(yīng)參數(shù)定義如下:
前端結(jié)果:
可以看出,在后端定義的實(shí)體中createTime
字段的類(lèi)型為LocalDateTime
,price
字段的值是12.1
只有一位小數(shù);
當(dāng)響應(yīng)到前端時(shí)createTime
字段的值時(shí)Long
類(lèi)型的時(shí)間戳,price
字段的值為12.10
并且是保留兩位小數(shù)的String
類(lèi)型值。
3、WebMvcConfigurationSupport踩坑說(shuō)明
如果項(xiàng)目中存在繼承WebMvcConfigurationSupport
的配置類(lèi),那么在定義DataConverterConfig(全局轉(zhuǎn)換器)
時(shí)的配置就需要做出調(diào)整,調(diào)整如下:
1、DataConverterConfig修改處理
DataConverterConfig
中刪除WebMvcConfigurer webMvcConfigurer()
方法,對(duì)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; } /** * 序列化實(shí)現(xiàn)BigDecimal轉(zhuǎn)化為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); } } } /** * 序列化實(shí)現(xiàn) LocalDateTime轉(zhuǎn)化為L(zhǎng)ong */ 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); } } } /** * 反序列化實(shí)現(xiàn) Long轉(zhuǎn)化為為L(zhǎng)ocalDateTime(只對(duì)GET請(qǐng)求無(wú)效),對(duì)于GET請(qǐng)求需要單獨(dú)訂單轉(zhuǎn)換器,比如: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繼承類(lèi)中處理
WebMvcConfigurationSupport
繼承類(lèi)中新增addFormatters()
方法與extendMessageConverters
方法以及注入ObjectMapper
,更改后代碼如下:
@Configuration public class WebMvcRegistrationsConfig extends WebMvcConfigurationSupport { @Resource private ObjectMapper serializingObjectMapper; /** * 添加自定義消息轉(zhuǎn)換器 */ @Override protected void addFormatters(FormatterRegistry registry) { // 添加時(shí)間戳轉(zhuǎn)日期類(lèi)型消息轉(zhuǎn)換器 registry.addConverter(new TimeStampToLocalDateConverter()); } @Override public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); converter.setObjectMapper(serializingObjectMapper); converters.add(0, converter); } }
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java編程在ICPC快速I(mǎi)O實(shí)現(xiàn)源碼
這篇文章主要介紹了Java Fast IO in ICPC實(shí)現(xiàn)源碼,具有一定參考價(jià)值,需要的朋友可以了解下。2017-09-09Swagger中@API?tags中含有中文異常問(wèn)題的解決
這篇文章主要介紹了Swagger中@API?tags中含有中文異常問(wèn)題的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。2022-01-01Java自帶的Http?Server實(shí)現(xiàn)設(shè)置返回值的類(lèi)型(content-type)
這篇文章主要介紹了Java自帶的Http?Server實(shí)現(xiàn)設(shè)置返回值的類(lèi)型(content-type),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11springboot1.X和2.X中如何解決Bean名字相同時(shí)覆蓋
這篇文章主要介紹了springboot1.X和2.X中如何解決Bean名字相同時(shí)覆蓋,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03運(yùn)行SpringBoot項(xiàng)目請(qǐng)求響應(yīng)流程分析以及404和500報(bào)錯(cuò)的解決辦法
這篇文章主要介紹了運(yùn)行Spring Boot項(xiàng)目請(qǐng)求響應(yīng)流程分析以及404和500報(bào)錯(cuò)的解決辦法,文中通過(guò)代碼示例和圖文講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-12-12基于Gradle搭建Spring?5.3.13-release源碼閱讀環(huán)境的詳細(xì)流程
這篇文章主要介紹了基于Gradle搭建Spring?5.3.13-release源碼閱讀環(huán)境,首先安裝jdk、gradle等一系列必要操作,本文通過(guò)實(shí)例代碼相結(jié)合給大家講解的非常詳細(xì),需要的朋友可以參考下2022-04-04