SpringBoot序列化、反序列化空字符串為null的三種方式
一、需求:接收前端傳入的""空字符串參數(shù),有時候我們需要把它轉(zhuǎn)為null
- SpringBoot項目
- 方式:①Jackson(推薦)、②切面+反射、③注解+切面+反射
- 后兩種方式,未做返回值的處理。
二、三種方式
1、Jackson正反序列化(推薦)
- StdConverter 和 JsonSerializer的區(qū)別
兩種方式都可以實現(xiàn)將空字符串修改為 null 值的邏輯,但它們之間有一些區(qū)別:
1. **繼承的類不同**:
- 使用 `StdConverter` 方式時,`StringToNullSerializer` 類繼承自 `StdConverter<String, String>`,并實現(xiàn)了 `convert` 方法。
- 使用 `JsonSerializer` 方式時,`StringToNullSerializer` 類直接繼承自 `JsonSerializer<String>`,并實現(xiàn)了 `serialize` 方法。
2. **接口和方法不同**:
- `StdConverter` 是 Jackson 庫中提供的用于定義轉(zhuǎn)換器的類,其中的 `convert` 方法用于將一個類型轉(zhuǎn)換為另一個類型。
- `JsonSerializer` 是 Jackson 庫中用于定制序列化邏輯的接口,其中的 `serialize` 方法用于將 Java 對象序列化為 JSON 數(shù)據(jù)。
3. **對于序列化過程的處理不同**:
- 在 `StdConverter` 方式中,你需要實現(xiàn) `convert` 方法來定義如何將空字符串轉(zhuǎn)換為 null 值。
- 在 `JsonSerializer` 方式中,你需要實現(xiàn) `serialize` 方法來定義如何將字段序列化為 JSON 數(shù)據(jù),并在其中進行空字符串轉(zhuǎn)換為 null 值的處理。
綜上所述,兩種方式都可以實現(xiàn)相同的功能,選擇哪一種方式取決于個人偏好以及代碼的整體結(jié)構(gòu)和風格。通常來說,如果只需要定制序列化邏輯而不需要轉(zhuǎn)換其他類型,直接實現(xiàn) `JsonSerializer` 接口可能會更清晰和簡潔。
- ENTITY
package com.cc.jxtd.entity;
import com.cc.jxtd.serializer.ConverterEmptyStringToNull;
import com.cc.jxtd.serializer.EmptyStringToNullDeserializer;
import com.cc.jxtd.serializer.ConverterEmptyStringToInteger0;
import com.cc.jxtd.serializer.EmptyStringToNullSerializer;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import lombok.Data;
import javax.naming.Name;
/**
* <p>請求,返回都用這個</p>
*
* @author --
* @since 2024/4/19
*/
@Data
public class UserCs {
private Long id;
//反序列化(前端請求):空字符串為null
@JsonDeserialize(using = EmptyStringToNullDeserializer.class)
private String name;
//反序列化(前端請求):轉(zhuǎn)換:為其他類型的值(轉(zhuǎn)換為int的0)
@JsonDeserialize(converter = ConverterEmptyStringToInteger0.class)
private Integer descConverter0;
//序列化(后端返回):空字符串為null
@JsonSerialize(using = EmptyStringToNullSerializer.class)
private String descSerialize;
//序列化(后端返回):轉(zhuǎn)換:空字符串轉(zhuǎn)為null
@JsonSerialize(converter = ConverterEmptyStringToNull.class)
private String descConverterNull;
}
- 序列化處理類
package com.cc.jxtd.serializer;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
/** 序列化:String的空轉(zhuǎn)為null
* @author --
* @since 2024/4/18
**/
public class EmptyStringToNullSerializer extends JsonSerializer<String> {
/**
* 序列化為null
*/
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider serializerProvider) throws IOException {
if (value == null || value.trim().isEmpty()) {
gen.writeNull();
}else {
gen.writeString(value);
}
}
}
- 反序列化處理類
package com.cc.jxtd.serializer;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException;
/** 反序列化:空字符串轉(zhuǎn)換為null
* @author --
* @since 2024/4/18
**/
public class EmptyStringToNullDeserializer extends JsonDeserializer<String> {
/**
* 反序列化為null
*/
@Override
public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String value = p.getValueAsString();
if (value == null || value.trim().isEmpty()) {
return null;
}
return value;
}
}
- 序列化-轉(zhuǎn)換1
package com.cc.jxtd.serializer;
import com.fasterxml.jackson.databind.util.StdConverter;
/** 序列化-轉(zhuǎn)換:將string的空轉(zhuǎn)為null
* @author --
* @since 2024/4/18
**/
public class ConverterEmptyStringToNull extends StdConverter<String, String> {
@Override
public String convert(String value) {
//把空的string轉(zhuǎn)為int的0
if (value == null || value.trim().isEmpty()) {
return null;
}
return value;
}
}
- 序列化-轉(zhuǎn)換2
package com.cc.jxtd.serializer;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.databind.util.StdConverter;
/** 序列化:1將string轉(zhuǎn)為int。
* 2轉(zhuǎn)換String的空或null -》 轉(zhuǎn)為Integer的0
* @author --
* @since 2024/4/18
**/
public class ConverterEmptyStringToInteger0 extends StdConverter<String, Integer> {
@Override
public Integer convert(String value) {
//把空的string轉(zhuǎn)為int的0
if (value == null || value.trim().isEmpty()) {
return 0;
}
return Integer.valueOf(value);
}
}
- Controller
package com.cc.jxtd.web.controller;
import com.cc.jxtd.entity.UserCs;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* <p></p>
*
* @author --
* @since 2024/4/19
*/
@RestController
@RequestMapping("/userCs")
public class UserController {
@PostMapping
public UserCs get(@RequestBody UserCs req){
System.out.println("請求參數(shù)-id:" + req.getId());
System.out.println("請求參數(shù)-name:" + req.getName());
System.out.println("請求參數(shù)-desc1:" + req.getDescSerialize());
System.out.println("請求參數(shù)-desc2:" + req.getDescConverterNull());
System.out.println("請求參數(shù)-desc3:" + req.getDescConverter0());
//返回:序列化
return req;
}
}
- 測試

2、切面+反射/3、注解+切面+反射
- 區(qū)別
2、切面+反射:所有空字符串的字段都轉(zhuǎn)為null
3、注解+切面+反射:只有打了@EmptyToNull的字段才會轉(zhuǎn)換
- 導入包
<!--spring-boot-starter-aop-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
- 切面
package com.cc.jxtd.aspect;
import com.cc.jxtd.annotation.EmptyToNull;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.util.Objects;
/** 切面
* @author --
*/
@Aspect
@Component
public class UpdateAspect {
private static final Logger logger = LoggerFactory.getLogger(UpdateAspect.class);
//切入點
@Pointcut("@annotation(com.cc.jxtd.annotation.OptConverter)")
public void validPointCut() {
}
/**
* 環(huán)繞修改參數(shù)
*/
@Around("validPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
Object[] args = point.getArgs();
Object arg = args[0];
//2、切面+反射:全部轉(zhuǎn)換
this.allEmptyToNull(arg);
//3、注解+切面+反射:部分轉(zhuǎn)換
// this.assignEmptyToNull(arg);
return point.proceed();
}
/**
* 設置請求參數(shù)中 所有字段的空值(如:String的空字符串)為null
* @param arg arg
*/
public void allEmptyToNull(Object arg) {
if (Objects.isNull(arg)) {
return;
}
Field[] fields = arg.getClass().getDeclaredFields();
for (Field field : fields) {
// 設置字段可訪問
field.setAccessible(true);
// 如果字段是 String 類型且值為空字符串,則設置為 null
if (field.getType() == String.class) {
try {
String value = (String) field.get(arg);
if (value != null && value.isEmpty()) {
field.set(arg, null);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
// 可以擴展其他類型的參數(shù)……
}
}
/** 指定空轉(zhuǎn)null
* @param arg arg
* @since 2024/4/18
**/
private void assignEmptyToNull(Object arg) {
if (Objects.isNull(arg)) {
return;
}
Field[] fields = arg.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(EmptyToNull.class)) {
// 設置字段可訪問
field.setAccessible(true);
// 如果字段是 String 類型且值為空字符串,則設置為 null
if (field.getType() == String.class) {
try {
String value = (String) field.get(arg);
if (value != null && value.isEmpty()) {
field.set(arg, null);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
// 可以擴展其他類型的參數(shù)……
}
}
}
}
- 注解
package com.cc.jxtd.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 轉(zhuǎn)換
* @author --
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OptConverter {
}
- 注解2
package com.cc.jxtd.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** 轉(zhuǎn)化空為null
* @author --
* @since 2024/4/18
**/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EmptyToNull {
}
- entity
package com.cc.jxtd.entity;
import com.cc.jxtd.serializer.ConverterEmptyStringToInteger0;
import com.cc.jxtd.serializer.ConverterEmptyStringToNull;
import com.cc.jxtd.serializer.EmptyStringToNullDeserializer;
import com.cc.jxtd.serializer.EmptyStringToNullSerializer;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import lombok.Data;
/**
* <p>請求,返回都用這個</p>
*
* @author --
* @since 2024/4/19
*/
@Data
public class UserCs2 {
private Long id;
private String name;
private String desc;
}
- controller
package com.cc.jxtd.web.controller;
import com.cc.jxtd.annotation.OptConverter;
import com.cc.jxtd.entity.UserCs;
import com.cc.jxtd.entity.UserCs2;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* <p></p>
*
* @author --
* @since 2024/4/19
*/
@RestController
@RequestMapping("/userCs")
public class UserController {
// @PostMapping
// public UserCs get(@RequestBody UserCs req){
// System.out.println("請求參數(shù)-id:" + req.getId());
// System.out.println("請求參數(shù)-name:" + req.getName());
// System.out.println("請求參數(shù)-DescSerialize:" + req.getDescSerialize());
// System.out.println("請求參數(shù)-DescConverterNull:" + req.getDescConverterNull());
// System.out.println("請求參數(shù)-DescConverter0:" + req.getDescConverter0());
//
// //返回:序列化
// return req;
// }
@OptConverter
@PostMapping
public UserCs2 get(@RequestBody UserCs2 req){
System.out.println("請求參數(shù)-id:" + req.getId());
System.out.println("請求參數(shù)-name:" + req.getName());
System.out.println("請求參數(shù)-desc:" + req.getDesc());
//返回:序列化
return req;
}
}
-
測試2

-
測試3



浙公網(wǎng)安備 33010602011771號