SpringBoot序列化、反序列化空字符串为null的三种方式
一、需求:接收前端传入的""空字符串参数,有时候我们需要把它转为null
- SpringBoot项目
- 方式:①Jackson(推荐)、②切面+反射、③注解+切面+反射
- 后两种方式,未做返回值的处理。
二、三种方式
1、Jackson正反序列化(推荐)
- StdConverter 和 JsonSerializer的区别
两种方式都可以实现将空字符串修改为 null 值的逻辑,但它们之间有一些区别:
1. **继承的类不同**:
- 使用 `StdConverter` 方式时,`StringToNullSerializer` 类继承自 `StdConverter<String, String>`,并实现了 `convert` 方法。
- 使用 `JsonSerializer` 方式时,`StringToNullSerializer` 类直接继承自 `JsonSerializer<String>`,并实现了 `serialize` 方法。
2. **接口和方法不同**:
- `StdConverter` 是 Jackson 库中提供的用于定义转换器的类,其中的 `convert` 方法用于将一个类型转换为另一个类型。
- `JsonSerializer` 是 Jackson 库中用于定制序列化逻辑的接口,其中的 `serialize` 方法用于将 Java 对象序列化为 JSON 数据。
3. **对于序列化过程的处理不同**:
- 在 `StdConverter` 方式中,你需要实现 `convert` 方法来定义如何将空字符串转换为 null 值。
- 在 `JsonSerializer` 方式中,你需要实现 `serialize` 方法来定义如何将字段序列化为 JSON 数据,并在其中进行空字符串转换为 null 值的处理。
综上所述,两种方式都可以实现相同的功能,选择哪一种方式取决于个人偏好以及代码的整体结构和风格。通常来说,如果只需要定制序列化逻辑而不需要转换其他类型,直接实现 `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;
//反序列化(前端请求):转换:为其他类型的值(转换为int的0)
@JsonDeserialize(converter = ConverterEmptyStringToInteger0.class)
private Integer descConverter0;
//序列化(后端返回):空字符串为null
@JsonSerialize(using = EmptyStringToNullSerializer.class)
private String descSerialize;
//序列化(后端返回):转换:空字符串转为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的空转为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;
/** 反序列化:空字符串转换为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;
}
}
- 序列化-转换1
package com.cc.jxtd.serializer;
import com.fasterxml.jackson.databind.util.StdConverter;
/** 序列化-转换:将string的空转为null
* @author --
* @since 2024/4/18
**/
public class ConverterEmptyStringToNull extends StdConverter<String, String> {
@Override
public String convert(String value) {
//把空的string转为int的0
if (value == null || value.trim().isEmpty()) {
return null;
}
return value;
}
}
- 序列化-转换2
package com.cc.jxtd.serializer;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.databind.util.StdConverter;
/** 序列化:1将string转为int。
* 2转换String的空或null -》 转为Integer的0
* @author --
* @since 2024/4/18
**/
public class ConverterEmptyStringToInteger0 extends StdConverter<String, Integer> {
@Override
public Integer convert(String value) {
//把空的string转为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("请求参数-id:" + req.getId());
System.out.println("请求参数-name:" + req.getName());
System.out.println("请求参数-desc1:" + req.getDescSerialize());
System.out.println("请求参数-desc2:" + req.getDescConverterNull());
System.out.println("请求参数-desc3:" + req.getDescConverter0());
//返回:序列化
return req;
}
}
- 测试

2、切面+反射/3、注解+切面+反射
- 区别
2、切面+反射:所有空字符串的字段都转为null
3、注解+切面+反射:只有打了@EmptyToNull的字段才会转换
- 导入包
<!--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() {
}
/**
* 环绕修改参数
*/
@Around("validPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
Object[] args = point.getArgs();
Object arg = args[0];
//2、切面+反射:全部转换
this.allEmptyToNull(arg);
//3、注解+切面+反射:部分转换
// this.assignEmptyToNull(arg);
return point.proceed();
}
/**
* 设置请求参数中 所有字段的空值(如: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();
}
}
// 可以扩展其他类型的参数……
}
}
/** 指定空转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();
}
}
// 可以扩展其他类型的参数……
}
}
}
}
- 注解
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;
/**
* 转换
* @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;
/** 转化空为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("请求参数-id:" + req.getId());
// System.out.println("请求参数-name:" + req.getName());
// System.out.println("请求参数-DescSerialize:" + req.getDescSerialize());
// System.out.println("请求参数-DescConverterNull:" + req.getDescConverterNull());
// System.out.println("请求参数-DescConverter0:" + req.getDescConverter0());
//
// //返回:序列化
// return req;
// }
@OptConverter
@PostMapping
public UserCs2 get(@RequestBody UserCs2 req){
System.out.println("请求参数-id:" + req.getId());
System.out.println("请求参数-name:" + req.getName());
System.out.println("请求参数-desc:" + req.getDesc());
//返回:序列化
return req;
}
}
测试2

测试3



SpringBoot序列化、反序列化空字符串为null的三种方式的更多相关文章
- C# 对象与JSON字符串互相转换的三种方式
C# 对象与JSON字符串互相转换的三种方式 JSON(JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交换格式. 关于内存对象和JSON字符串的相互转换, ...
- JSON字符串互相转换的三种方式和性能比较
C# 对象与JSON字符串互相转换的三种方式 JSON(JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交换格式. 关于内存对象和JSON字符串的相互转换, ...
- 总结springboot开启mybatis驼峰命名自动映射的三种方式
方式一:通过springboot的配置文件application.yml mybatis: configuration: map-underscore-to-camel-case: true 此方式是 ...
- SpringBoot:CORS处理跨域请求的三种方式
一.跨域背景 1.1 何为跨域? Url的一般格式: 协议 + 域名(子域名 + 主域名) + 端口号 + 资源地址 示例: https://www.dustyblog.cn:8080/say/Hel ...
- js中将字符串转为JSON的三种方式
1.eval方式解析,恐怕这是最早的解析方式了.如下: function strToJson(str){ var json = eval('(' + str + ')'); return json; ...
- Java空字符串与null的区别和判断字符串是否为空的方法
Java空字符串与null的区别: 1.类型null表示的是一个对象的值,而并不是一个字符串.例如声明一个对象的引用,String a = null ;""表示的是一个空字符串,也 ...
- Oracle坑之-空字符串与NULL
空字符串与NULL 首先有如下代码 SELECT * FROM Pdc_DataDomain DD INNER JOIN Pdc_DD_Table DDT ON DD.DataDomainID = D ...
- SpringMVC空字符串转为null
空字符串转为null 现在我遇到这样一个需求,那就是我想要吧前端传过来的值变为空,因为所谓前端的校验,其实都不是校验,如果前端传给后台一个表单,可是表单未填入值,我们后台进行判断的时候 既需要判断nu ...
- Java进阶(二十一)java 空字符串与null区别
java 空字符串与null区别 1.类型 null表示的是一个对象的值,而并不是一个字符串.例如声明一个对象的引用,String a = null ; ""表示的是一个空字符串, ...
- Access空字符串和Null值
什么是空字符串和Null值: Microsoft Access可以区分两种类型的空值.因为在某些情况下,字段为空,可能是因为信息目前无法获得,或者字段不适用于某一特定的记录.例如,表中有一个“电话号码 ...
随机推荐
- B站框框老师+宋浩老师概率论视频课笔记,宋浩老师数理统计视频课笔记
只做理解类记录,哪个知识点忘了去看视频.前四章是概率,看的框框老师. 概率论 1.随机试验:可重复性.可预知性.不确定性 2.样本空间:随机试验E的所有可能结果,记为S或Ω 3.样本点:样本空间中的每 ...
- python---nltk工具包安装
先在pycharm里安装nltk cmd进入Python输入 import nltk nltk.download()如果下载失败在github上下载语料库:https://github.com/nlt ...
- 攻防世界 Replace Reverse二星题
解题过程中,虽然解出来了,但是磕磕绊绊犯了一些错误,记录一下 分析过程 PE查壳 有一个upx壳,最下面给了脱壳提示: upx.exe -d Replace.exe 脱壳结束,丢到IDA里,SHIF+ ...
- 5 JavaScript变量提升
5 变量提升 看以下代码, 或多或少会有些问题的. function fn(){ console.log(name); var name = '大马猴'; } fn() 发现问题了么. 这么写代码, ...
- #二进制拆分,矩阵乘法#洛谷 6569 [NOI Online #3 提高组] 魔法值
题目 分析 考虑一个点的权值能被统计到答案当且仅当其到1号点的路径条数为奇数条. 那么设 \(dp[i][x][y]\) 表示从 \(x\) 到 \(y\) 走 \(i\) 步路径条数的奇偶性, 这个 ...
- #SG函数,记忆化搜索#HDU 4111 Alice and Bob
题目 Alice和Bob两个好朋友又开始玩取石子了. 游戏开始时,有\(n\)堆石子排成一排,然后他们轮流操作(Alice先手),每次操作时从下面的规则中任选一个: ·从某堆石子中取走一个 ·合并任意 ...
- 【Kotlin】扩展属性、扩展函数
1 类的扩展 Kotlin 提供了扩展类或接口的操作,而无需通过类继承或使用装饰器等设计模式,来为某个类添加一些额外的属性或函数,我们只需要通过一个被称为扩展的特殊声明来完成.通过这种机制,我们可 ...
- 报表如何批量导出成 excel 文件
需求说明 报表展现后可以通过工具栏中的导出按钮将当前展现的报表导出成 excel 文件,但是在实际使用中通常会要求报表不需要展现,直接通过一些操作将报表导出成 excel 文件,并且往往会要求批量导出 ...
- sql 语句系列(用魔法打败魔法)[八百章之第十章]
前言 下面是一些常规的运维手段. 查询每个表的行数 SELECT a.name, a.object_id, b.rows, b.index_id FROM sys.tables AS a INNER ...
- python数据库迁移
实际操作命令 1,python 文件.py db init 2,python xx.py db migrate -m '版本描述' 3,python xx.py db upgrade 4,python ...