Java前后端任意參數(shù)類型轉(zhuǎn)換方式(Date、LocalDateTime、BigDecimal)
1、前言
在前后端進(jìn)行數(shù)據(jù)交互時(shí),對(duì)于日期往往是通過時(shí)間戳進(jìn)行交互,或者是Doule、BigDecimal等格式進(jìn)行格式化保留固定小數(shù)點(diǎn)。
比如:Double與String、BigDecimal與String、Long與Date、Long與LocalDateTime
主要通過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)換的類型才會(huì)進(jìn)入自定義轉(zhuǎn)換器中,比如:轉(zhuǎn)換的字段類型為BigDecimal,如果請(qǐng)求/響應(yīng)的實(shí)體中不包含BigDecimal類型,那么就不會(huì)進(jìn)行到轉(zhuǎn)換器中
在SpringBoot中可以通過配置實(shí)現(xiàn)全局參數(shù)的字段轉(zhuǎn)換,或者使用注解標(biāo)注進(jìn)行指定字段的轉(zhuǎn)換生效或者轉(zhuǎn)換失效;
2、配置類型全局轉(zhuǎn)換器
代碼以實(shí)現(xiàn)后端格式化BigDecimal數(shù)據(jù)和前后端交互LocalDateTime轉(zhuǎn)Long兩種形式進(jìn)行演示,實(shí)際中可以根據(jù)情況進(jìn)行修改轉(zhuǎn)換方式,實(shí)現(xiàn)任意類型的序列化轉(zhuǎn)換;
2.1、定義類型全局轉(zhuǎn)換器
該類中的反序列,對(duì)Get請(qǐng)求無效,會(huì)出現(xiàn)類型轉(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()方法的類型
// 如果有需要?jiǎng)t進(jìn)行開啟
// 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)求無效)
*/
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 類中的定義的反序列化轉(zhuǎn)換器,對(duì)Get請(qǐng)求無效,會(huì)出現(xiàn)類型轉(zhuǎn)換失敗的情況,對(duì)于Get請(qǐng)求需要單獨(dú)根據(jù)定義反序列化轉(zhuǎn)換器,比如:此處用到的Long轉(zhuǎn)LocalDateTime反序列化轉(zhuǎn)換,如果有其他類型就需要定義其他的反序列化轉(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í)間類型轉(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類型的時(shí)間戳

結(jié)果:
雖然前端傳入時(shí)參數(shù)為long類型的時(shí)間戳,但是后端打印出的數(shù)據(jù)格式為LocalDateTime,表示反序列化是轉(zhuǎn)換成功;

2、序列化測(cè)試
響應(yīng)參數(shù)定義如下:

前端結(jié)果:
可以看出,在后端定義的實(shí)體中createTime字段的類型為LocalDateTime,price字段的值是12.1只有一位小數(shù);
當(dāng)響應(yīng)到前端時(shí)createTime字段的值時(shí)Long類型的時(shí)間戳,price字段的值為12.10并且是保留兩位小數(shù)的String類型值。

3、WebMvcConfigurationSupport踩坑說明
如果項(xiàng)目中存在繼承WebMvcConfigurationSupport的配置類,那么在定義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)求無效),對(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繼承類中處理
WebMvcConfigurationSupport繼承類中新增addFormatters()方法與extendMessageConverters方法以及注入ObjectMapper,更改后代碼如下:
@Configuration
public class WebMvcRegistrationsConfig extends WebMvcConfigurationSupport {
@Resource
private ObjectMapper serializingObjectMapper;
/**
* 添加自定義消息轉(zhuǎn)換器
*/
@Override
protected void addFormatters(FormatterRegistry registry) {
// 添加時(shí)間戳轉(zhuǎn)日期類型消息轉(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快速IO實(shí)現(xiàn)源碼
這篇文章主要介紹了Java Fast IO in ICPC實(shí)現(xiàn)源碼,具有一定參考價(jià)值,需要的朋友可以了解下。2017-09-09
Java自帶的Http?Server實(shí)現(xiàn)設(shè)置返回值的類型(content-type)
這篇文章主要介紹了Java自帶的Http?Server實(shí)現(xiàn)設(shè)置返回值的類型(content-type),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11
springboot1.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ò)的解決辦法,文中通過代碼示例和圖文講解的非常詳細(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等一系列必要操作,本文通過實(shí)例代碼相結(jié)合給大家講解的非常詳細(xì),需要的朋友可以參考下2022-04-04

