Springboot实现jackson⾃定义字段过滤⼀、问题描述
当VO应⽤于不同的请求接⼝,可能需要的请求字段不⼀样
@Getter
@Setter
@ToString
@JsonInclude(JsonInclude.Include.NON_NULL)
public class User {
private Integer id;
private String name;
private String sex;
private String adress;
private String phoneNumber;
private Date gmtCreate;
private Date gmtModified;
}
如上VO,对于插⼈接⼝,如下
public ApiResult addUser(@RequestBody User user) {
ApiResult apiResult = ApiResult.SUCCESS();
List<User> users = new ArrayList<User>();
users.add(user);
apiResult.setRows(users);
return apiResult;
}
可能只需要除id,gmtCreate,gmtModified的其它的字段,
但是对于edit接⼝,需要主键id,
public ApiResult editUser(@RequestBody User user) {
ApiResult apiResult = ApiResult.SUCCESS();
List<User> users = new ArrayList<User>();
users.add(user);
apiResult.setRows(users);
return apiResult;
}
当VO应⽤于同⼀接⼝,请求VO与返回VO使⽤同⼀个类,所需必要字段不同
public ApiResult listUsers(@RequestBody User user) {
ApiResult apiResult = ApiResult.SUCCESS();
List<User> users = new ArrayList<User>();
User user1 = new User();
user1.setId(1);
user1.setAdress("dd");
user1.setName("dongzhi");
user1.setPhoneNumber("15859637**8");
user1.setSex("male");
users.add(user1);
User user2 = new User();
user2.setId(1);普遍⽴场ObjectMapperaddMixIn(Class<?>他融个铜,
user2.setName("zhang");
user2.setPhoneNumber("158596372**7");
user2.setSex("female");
users.add(user2);
apiResult.setRows(users);
return apiResult;
}
针对此情况,⼊参可能只需要id参数,⽽返回的参数可能需要全部字段,或者根据需求返回需要字段。
**针对上述情况,现有的Jackson注解⽆法完成。如@JsonIgnore等
因此需要基于此需求,利⽤Jackson内置机制,,利⽤AOP,注解的⽅式,进⾏指定字段的过滤。
⼆、实现⽅式
结合@JsonFilter 与 public ObjectMapper addMixIn(Class<?> target, Class<?> mixinSource)⽅法。@JsonFilter可以实现⾃定义字段过滤,主要⽤到其中两个⽅法
if (isInclude) {
objectMapper.setFilterProvider(new SimpleFilterProvider().addFilter(filterId,
SimpleBeanPropertyFilter.filterOutAllExcept(toBeFilterFields)));
} else {
objectMapper.setFilterProvider(new SimpleFilterProvider().addFilter(filterId,
SimpleBeanPropertyFilter.serializeAllExcept(toBeFilterFields)));
}
filterOutAllExcept:序列化指定字段
serializeAllExcept:除指定的字段都要序列化
addMixIn 可以实现要过滤的Vo和@JsonFilter解耦;
不使⽤⽤addMixIn,需要在VO加注解@JsonFilter,⽰例代码()如下⽅式如下:
//"ID-TITLE"为filter 的id
@JsonFilter("ID-TITLE")
class Article {
private String id;
private String title;
private String content;
// ... getter/setter
}
// Demo
class Demo {
public void main(String args[]) {
ObjectMapper mapper = new ObjectMapper();
// SimpleBeanPropertyFilter.filterOutAllExcept("id,title")
// 过滤除了 id,title 以外的所有字段,也就是序列化的时候,只包含 id 和 title
filter过滤对象数组
mapper.setFilterProvider(new SimpleFilterProvider().addFilter("ID-TITLE",
SimpleBeanPropertyFilter.filterOutAllExcept("id,title")));
String filterOut = mapper.writeValueAsString(new Article());
mapper = new ObjectMapper();
// SimpleBeanPropertyFilter.serializeAllExcept("id,title")
// 序列化所有字段,但是排除 id 和 title,也就是除了 id 和 title之外,其他字段都包含进 json
mapper.setFilterProvider(new SimpleFilterProvider().addFilter("ID-TITLE",
SimpleBeanPropertyFilter.serializeAllExcept(filter.split("id,title"))));
String serializeAll = mapper.writeValueAsString(new Article());
System.out.println("filterOut:" + filterOut);
System.out.println("serializeAll :" + serializeAll);
}
}
利⽤addMixIn⽅法⽰例()
封装json转换
通过上⾯的代码,我们发现,可以使⽤ setFilterProvider 来灵活的处理需要过滤的字段。不过上⾯的⽅法还有⼀些缺陷就是,还是要在原来的 model 上加注解,这⾥我们使⽤ ObjectMapper.addMixIn(Class<?> type, Class<?> mixinType) ⽅法,这个⽅法就是讲两个类的注解混
合,让第⼀个参数的类能够拥有第⼆个参数类的注解。让需要过滤的 model 和 @JsonFilter 注解解除耦合
s.server.json;
import com.fasterxml.jackson.annotation.JsonFilter;
import com.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
/**
* depend on jackson
* @author Diamond
*/
public class CustomerJsonSerializer {
static final String DYNC_INCLUDE = "DYNC_INCLUDE";
static final String DYNC_FILTER = "DYNC_FILTER";
ObjectMapper mapper = new ObjectMapper();
@JsonFilter(DYNC_FILTER)
interface DynamicFilter {
}
}
@JsonFilter(DYNC_INCLUDE)
interface DynamicInclude {
}
/
**
* @param clazz 需要设置规则的Class
* @param include 转换时包含哪些字段
* @param filter 转换时过滤哪些字段
*/
public void filter(Class<?> clazz, String include, String filter) {
if (clazz == null) return;
if (include != null && include.length() > 0) {
mapper.setFilterProvider(new SimpleFilterProvider().addFilter(DYNC_INCLUDE,
SimpleBeanPropertyFilter.filterOutAllExcept(include.split(","))));
mapper.addMixIn(clazz, DynamicInclude.class);
} else if (filter !=null && filter.length() > 0) {
mapper.setFilterProvider(new SimpleFilterProvider().addFilter(DYNC_FILTER,
SimpleBeanPropertyFilter.serializeAllExcept(filter.split(","))));
mapper.addMixIn(clazz, DynamicFilter.class);
}
}
public String toJson(Object object) throws JsonProcessingException {
return mapper.writeValueAsString(object);
}
}
我们之前的 Demo 可以变成:
/
/ Demo
class Demo {
public void main(String args[]) {
CustomerJsonSerializer cjs= new CustomerJsonSerializer();
// 设置转换 Article 类时,只包含 id, name
cjs.filter(Article.class, "id,name", null);
String include = Json(new Article());
cjs = new CustomerJsonSerializer();
// 设置转换 Article 类时,过滤掉 id, name
cjs.filter(Article.class, null, "id,name");
String filter = Json(new Article());
System.out.println("include: " + include);
System.out.println("filter: " + filter);
}
}
输出结果
include: {id: "", title: ""}
filter: {content:""}
三、实现步骤
考虑利⽤Spring AOP机制,以及⽅法级别注解,可以实现在特定⽅法上,针对特定类的特定字段进⾏过滤,主要有三种情况,
只过滤请求VO
过滤返回VO的字段
同时过滤请求VO和返回VO
实现基本功能:只针对VO⽐较单⼀的情况进⾏过滤,对于VO嵌套其它VO的情况,暂时未做处理,对于继承字段可以进⾏过滤。
当前仅⽀持⼊参为⼀个参数,且为对象的情况,其它情况暂未考虑。
⼊参Vo和出餐VO可以不是同⼀个VO对象
3.1 注解定义
package com.learn.jacksonmon.annotation;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Documented
@Retention(RUNTIME)
@Target({ FIELD, METHOD })
public @interface JacksonFilters {
JacksonFilter[] value();
}

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。