一、需求:接收前端传入的""空字符串参数,有时候我们需要把它转为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` 接口可能会更清晰和简洁。 |
| 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; |
| |
| |
| |
| |
| |
| |
| |
| @Data |
| public class UserCs { |
| |
| private Long id; |
| |
| |
| @JsonDeserialize(using = EmptyStringToNullDeserializer.class) |
| private String name; |
| |
| |
| @JsonDeserialize(converter = ConverterEmptyStringToInteger0.class) |
| private Integer descConverter0; |
| |
| |
| @JsonSerialize(using = EmptyStringToNullSerializer.class) |
| private String descSerialize; |
| |
| |
| @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; |
| |
| |
| |
| |
| |
| public class EmptyStringToNullSerializer extends JsonSerializer<String> { |
| |
| |
| |
| |
| @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; |
| |
| |
| |
| |
| |
| public class EmptyStringToNullDeserializer extends JsonDeserializer<String> { |
| |
| |
| |
| |
| @Override |
| public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { |
| String value = p.getValueAsString(); |
| if (value == null || value.trim().isEmpty()) { |
| return null; |
| } |
| return value; |
| } |
| |
| } |
| package com.cc.jxtd.serializer; |
| |
| import com.fasterxml.jackson.databind.util.StdConverter; |
| |
| |
| |
| |
| |
| public class ConverterEmptyStringToNull extends StdConverter<String, String> { |
| |
| @Override |
| public String convert(String value) { |
| |
| if (value == null || value.trim().isEmpty()) { |
| return null; |
| } |
| return value; |
| } |
| |
| } |
| package com.cc.jxtd.serializer; |
| |
| import com.fasterxml.jackson.annotation.JsonPropertyOrder; |
| import com.fasterxml.jackson.databind.util.StdConverter; |
| |
| |
| |
| |
| |
| |
| public class ConverterEmptyStringToInteger0 extends StdConverter<String, Integer> { |
| |
| @Override |
| public Integer convert(String value) { |
| |
| if (value == null || value.trim().isEmpty()) { |
| return 0; |
| } |
| return Integer.valueOf(value); |
| } |
| |
| } |
| 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; |
| |
| |
| |
| |
| |
| |
| |
| @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的字段才会转换 |
| |
| <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; |
| |
| |
| |
| |
| @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]; |
| |
| this.allEmptyToNull(arg); |
| |
| |
| |
| return point.proceed(); |
| } |
| |
| |
| |
| |
| |
| public void allEmptyToNull(Object arg) { |
| if (Objects.isNull(arg)) { |
| return; |
| } |
| Field[] fields = arg.getClass().getDeclaredFields(); |
| for (Field field : fields) { |
| |
| field.setAccessible(true); |
| |
| 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(); |
| } |
| } |
| |
| |
| } |
| } |
| |
| |
| |
| |
| |
| 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); |
| |
| 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; |
| |
| |
| |
| |
| |
| @Target(ElementType.METHOD) |
| @Retention(RetentionPolicy.RUNTIME) |
| public @interface OptConverter { |
| |
| } |
| 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; |
| |
| |
| |
| |
| |
| @Target(ElementType.FIELD) |
| @Retention(RetentionPolicy.RUNTIME) |
| public @interface EmptyToNull { |
| |
| } |
| 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; |
| |
| |
| |
| |
| |
| |
| |
| @Data |
| public class UserCs2 { |
| |
| private Long id; |
| |
| private String name; |
| |
| private String desc; |
| |
| } |
| 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; |
| |
| |
| |
| |
| |
| |
| |
| @RestController |
| @RequestMapping("/userCs") |
| public class UserController { |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| @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; |
| } |
| |
| } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!