Jackson教程
JsonFilter注解过滤字段

通过《Jackson忽略字段不序列化字段的3种方法》一讲我们知道,JsonIgnore和JsonIgnoreProperties都可以用来忽略掉指定的字段。

这两个注解有个共同点,就是都在添加注解后就指明了需要过滤的字段。如果想要在序列化的时候,才指定需要过滤的字段,那么可以使用JsonFilter注解来实现。

本篇内容基于Jackson 2.11.2版本,马上开始学习吧。

过滤类的字段

为需要过滤字段的类添加JsonFilter注解。

@JsonFilter("myFilter")
public class Animal {
 
	private String name;
	private int sex;
	private Integer weight;
	
	// 省略getter、setter方法
 
	@Override
	public String toString() {
		return "Animal [name=" + name + ", sex=" + sex + ", weight=" + weight + "]";
	}
 
}

忽略指定字段

创建一个不序列化sex和weight的过滤器SimpleBeanPropertyFilter,再将这个过滤器和ID为myFilter的注解进行关联,最后将过滤器应用于ObjectMapper。

最终的效果,就是使得被@JsonFilter("myFilter")注解的类,在类对象被序列化时,不序列化sex和weight。

/**
 * 忽略指定的字段
 * 
 * @throws JsonProcessingException
 */
@Test
public void filterExclude() throws JsonProcessingException {
	Animal animal = new Animal();
	animal.setName("sam");
	animal.setSex(1);
	animal.setWeight(100);
	
	ObjectMapper mapper = new ObjectMapper();
	
	// 创建一个不序列化sex和weight的过滤器
	SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter.serializeAllExcept("sex", "weight");
	// 将上面的过滤器和ID为myFilter的注解进行关联
    FilterProvider filters = new SimpleFilterProvider().addFilter("myFilter", filter);
 
	System.out.println(mapper.writer(filters).writeValueAsString(animal));
}

执行结果:

{"name":"sam"}

序列化指定的字段

SimpleBeanPropertyFilter.serializeAllExcept用于排除掉不需要序列化的字段,对应的SimpleBeanPropertyFilter.filterOutAllExcept用于指定需要序列化的字段。前者相当于排除,而后者则是包含。

/**
 * 序列化指定的字段
 * 
 * @throws JsonProcessingException
 */
@Test
public void filterInclude() throws JsonProcessingException {
	Animal animal = new Animal();
	animal.setName("sam");
	animal.setSex(1);
	animal.setWeight(100);
	
	ObjectMapper mapper = new ObjectMapper();
	
	// 创建一个只序列化sex和weight的过滤器
	SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter.filterOutAllExcept("sex", "weight");
	// 将上面的过滤器和ID为myFilter的注解进行关联
	FilterProvider filters = new SimpleFilterProvider().addFilter("myFilter", filter);
	
	System.out.println(mapper.writer(filters).writeValueAsString(animal));
}

执行结果

{"sex":1,"weight":100}

过滤对象的字段

如果对一个类声明了JsonFilter注解,那么任何引用到该类的地方都可以使用到该注解。

有时候,我们只是想针对引用该类的字段进行过滤,这种情况可以直接在该字段上面进行注解。

下面为AnimalWithOwner的People类型的字段owner添加JsonFilter注解,实际上是作用于owner对象的字段。

public class AnimalWithOwner {
 
	private String name;
	private int sex;
	private Integer weight;
	@JsonFilter("myFilter")
	private People owner;
	
	// 省略setter、getter方法
 
	@Override
	public String toString() {
		return "AnimalJsonFilterOnField [name=" + name + ", sex=" + sex + ", weight=" + weight + ", owner=" + owner
		        + "]";
	}
}
 
public class People {
	private String name;
	private int age;
 
	// 省略setter、getter方法
 
	@Override
	public String toString() {
		return "People [name=" + name + ", age=" + age + "]";
	}
}
/**
 * 作用于某个类型的字段
 * 
 * @throws JsonProcessingException
 */
@Test
public void filterIncludeOnField() throws JsonProcessingException {
	AnimalWithOwner animal = new AnimalWithOwner();
	animal.setName("sam");
	animal.setSex(1);
	animal.setWeight(100);
	
	People owner = new People();
	owner.setName("fanny");
	owner.setAge(26);
	animal.setOwner(owner);
	
	ObjectMapper mapper = new ObjectMapper();
	
	SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter.filterOutAllExcept("name");
	FilterProvider filters = new SimpleFilterProvider().addFilter("myFilter", filter);
	
	System.out.println(mapper.writer(filters).writeValueAsString(animal));
}

执行结果:

{"name":"sam","sex":1,"weight":100,"owner":{"name":"fanny"}}

自定义过滤器

前面在过滤字段的时候,我们使用的是SimpleBeanPropertyFilter.serializeAllExcept和SimpleBeanPropertyFilter.filterOutAllExcept。

这两个方法,实际上是使用了预先定义好的两个过滤器,分别是SerializeExceptFilter和FilterExceptFilter。

当已有的过滤器满足不了需求的时候,可以参考现有的过滤器,通过自定义过滤器来实现。

先创建一个自定义的过滤器类,然后继承SimpleBeanPropertyFilter,实现两个include方法即可。

/**
 * 自定义过滤器.
 *
 */
public static class CustomExceptFilter extends SimpleBeanPropertyFilter {
 
	@Override
	protected boolean include(BeanPropertyWriter writer) {
		return filter(writer.getName(), writer.getType());
	}
 
	@Override
	protected boolean include(PropertyWriter writer) {
		return filter(writer.getName(), writer.getType());
	}
	
	private boolean filter(String fieldName, JavaType fieldType) {
//			System.out.println(fieldName + " " + fieldType.getTypeName() + " " + fieldType.getRawClass());
			// 排除以n开头的字段,以及int类型的字段
			if (fieldName.startsWith("n") || fieldType.getRawClass() == int.class) {
				return false;				
			}
			return true;
		}
	}

在这个过滤器中,使用到的是字段名和字段类型。通过一个if语句,排除掉以n开头的字段,以及int类型的字段。

返回true表示包含该字段,返回false表示忽略掉该字段。

/**
 * 自定义过滤器. 通过继承SimpleBeanPropertyFilter,根据实际需要编写过滤规则.
 * 
 * @throws JsonProcessingException
 */
@Test
public void customFilter() throws JsonProcessingException {
	Animal animal = new Animal();
	animal.setName("sam");
	animal.setSex(1);
	animal.setWeight(100);
	
	ObjectMapper mapper = new ObjectMapper();
	
	SimpleBeanPropertyFilter filter = new CustomExceptFilter();
	FilterProvider filters = new SimpleFilterProvider().addFilter("myFilter", filter);
	
	System.out.println(mapper.writer(filters).writeValueAsString(animal));
}

执行结果:

{"weight":100}

以n开头的是name字段,int类型的是sex字段,所以最后只剩下weight需要序列化。

小结

使用JsonIgnore和JsonIgnoreProperties都可以用来忽略掉指定的字段,这种方法简单实用,但不够灵活。

如果需要更灵活,动态的过滤字段时,可以考虑使用JsonFilter注解。

JsonFilter注解的几个优点:

1.在序列化的时候再指定需要过滤的字段; 2.支持通过包含和排除两方式来过滤字段; 3.通过自定义过滤器,实现更加复杂的过滤规则。

参考

https://github.com/FasterXML/jackson (opens in a new tab)

https://www.logicbig.com/tutorials/misc/jackson/json-filter-annotation.html (opens in a new tab)