Spring Boot中配置Jackson序列化和反序列化字段过滤

4,372 阅读4分钟

在开发接口的时候,我们通常想要过滤一些字段不返回给前端。

例如前端需要查询一个用户,但是用户User类中通常有密码字段,我们不想返回密码字段给前端,应当怎么办呢?我们只需要借助几个Jackson注解即可完美解决。

不过首先我们需要了解一些基本知识。

1,什么是序列化/反序列化

Java的实例一般存在于内存里面,如果想要把这个对象在网络上传输或者是存放到本地,那么必须要使这个对象序列化,也就是说序列化把一个内存中的对象数据转换为了可传输存储的形式,例如字符串等等。

在Spring Boot中,通常前端会以JSON格式把对象传给后端(例如用户登录,前端把用户信息组装为JSON传给后端,后端解析为User对象),后端(Spring Boot)会将这个JSON解析为对象/实例,这就是反序列化的过程。(JSON字符串 -> Java对象

以及前端有时需要发送请求向后端查询对象(例如前端查户列表),这时后端接收了请求就会去数据库查询,查询到的数据组装为Java对象,然后再把这些Java对象转换为JSON字符串形式返回给前端,这就是序列化过程。(Java对象 -> JSON字符串

序列化和反序列化不止这些,上面只是简要地说了在Spring Boot中常见的对象传输方式,我们只需了解这些即可。

2,配置Jackson过滤一些不必要字段

(1) @JsonIgnore注解忽略字段

例如User类中,我们可以使用@JsonIgnore注解来忽略掉某些字段,这个注解标注于类的属性上,例如:

package com.gitee.swsk33.jacksonserializedemo.dataobject;

import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 用户类
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

	/**
	 * 主键id
	 */
	private int id;

	/**
	 * 用户名
	 */
	private String username;

	/**
	 * 密码
	 */
	@JsonIgnore
	private String password;

}

可见password密码字段上面标注了@JsonIgnore,这样序列化和反序列化都会忽略password字段。

我们编写一个RestController接口来试试:

package com.gitee.swsk33.jacksonserializedemo.api;

import com.gitee.swsk33.jacksonserializedemo.dataobject.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

@Slf4j
@RestController
@RequestMapping("/api/user")
public class UserAPI {

	@GetMapping("/get")
	public User get() {
		return new User(1, "哈哈哈", "12345678");
	}

	@PostMapping("/receive")
	public String receive(@RequestBody User user) {
		log.info(user.toString());
		return "已接收对象!";
	}

}

然后借助Apifox请求测试工具对路径/api/user/get发起请求试试:

image.png

可见虽然我们在代码中设定了User对象的password属性,但是由于该属性标注了@JsonIgnore注解,这样这个字段在序列化时就会被忽略掉。

(2) @JsonIgnoreProperties注解更灵活配置

问题来了,这样确实解决了前端查询用户时过滤掉密码字段,但是前端发送用户信息(用户登录注册时)后端反序列化时也过滤了密码字段。

我们可以先向上述/api/user/receive路径发起请求传递一个用户对象:

image.png

打印结果:

image.png

可见@JsonIgnore标注的字段,会在序列化和反序列化时都被忽略,那么如果我只想让该字段在序列化时被忽略,反序列化时不被忽略怎么办呢?

这时,我们可以使用@JsonIgnoreProperties注解,这个注解更强大,它标注于类上面,它有如下属性:

  • value 被配置的字段名,为数组类型,表示要配置的字段
  • ignoreUnknown 忽略未知属性,配置为true则会忽略未知属性,默认不忽略
  • allowGetters 设定被配置的字段(即value属性中的字段)的序列化策略,默认不允许被序列化,配置为true时,上述被配置的字段会被序列化
  • allowSetters 设定被配置的字段(即value属性中的字段)的反序列化策略,默认不允许被反序列化,配置为true时,上述被配置的字段会被反序列化

对于上述User类,我们需要在用户登录注册(即前端传送数据给后端)时后端仍要对password字段进行读取和反序列化,而前端查询用户时(后端返回数据给前端)过滤掉password字段,通过@JsonIgnoreProperties配置如下:

package com.gitee.swsk33.jacksonserializedemo.dataobject;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 用户类
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonIgnoreProperties(value = "password", allowSetters = true)
public class User {

	/**
	 * 主键id
	 */
	private int id;

	/**
	 * 用户名
	 */
	private String username;

	/**
	 * 密码
	 */
	private String password;

}

可见我们注解中配置过滤password字段,并允许这个字段被反序列化,这样就实现了仅仅不传输密码字段给前端。

我们可以测试一下,首先是获取用户对象:

image.png

可见password字段没有被序列化。

然后提交一个用户试试:

image.png

结果:

image.png

可见后端接收到请求并反序列化时,没有忽略password字段。