动态忽略 Jackson 中的字段

1、简介

本文将带你了解如何在 Jackson 中通过 @JsonFilter@JsonViewJackson Mixin(混合) 实现动态忽略字段。

2、项目设置

添加 Jackson 库:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.17.2</version>
</dependency>

最新版本可在 此处 找到。

3、使用 @JsonFilter

第一种方法是通过 @JsonFilter 注解指定在序列化过程中使用的过滤器。

使用 @JsonFilter 对类进行注解:

@JsonFilter("publicFilter")  // 指定 Filter 名称
public class UserWithFilter {
    private Long id;
    private String name;
    //Getter/Setter 忽略
}

然后,动态配置 ObjectMapper,并注册 PropertyFilter 来序列化除 id 之外的所有字段:

SimpleFilterProvider filterProvider = new SimpleFilterProvider();
// 注册 Filter,指定名字
filterProvider.addFilter("publicFilter", SimpleBeanPropertyFilter.serializeAllExcept("id"));
ObjectMapper objectMapper = new ObjectMapper().setFilterProvider(filterProvider);

然后,使用 objectMapper 序列化 UserWithFilter 对象:

UserWithFilter user = new UserWithFilter(1000L, "John");
String result = objectMapper.writeValueAsString(user);
assertThat(result).contains("John");
assertThat(result).doesNotContain("1000")

这种方法具有极大的灵活性,因为我们可以在运行时选择序列化哪些字段。

遗憾的是,这种方法不适用于动态反序列化对象。如下,使用相同的 ObjectMapper 进行反序列化,对象中的 id 字段会被反序列化:

String json = "{\"id\":1000,\"name\":\"John\"}";
UserWithFilter result = objectMapper.readValue(json, UserWithFilter.class);
assertEquals(1000L, result.getId());
assertEquals("John", result.getName());

4、使用 @JsonView 进行有条件忽略

@JsonView 是一种通过定义不同视图来控制字段可见性的方法。与前一种方法不同,我们需要在编译时定义序列化选项:

public class UserWithView {
    @JsonView(InternalView.class)
    private Long id;
    @JsonView(PublicView.class)
    private String name;
    // Getter / Setter 忽略

    public interface PublicView {}
    public interface InternalView extends PublicView {}
}

在运行时,我们可以通过配置 ObjectMapper 来选择序列化对象时要使用的 JsonView

ObjectWriter objectWriter = new ObjectMapper().writerWithView(UserWithView.PublicView.class);

在这种情况下,结果不包含 ID 字段,因为 ID 只包含在 InternalView 中:

String result = objectWriter.writeValueAsString(user);
assertThat(result).contains("John");
assertThat(result).doesNotContain("1000")

@JsonView 动态方法也适用于将 Json 反序列化为对象:

String json = "{\"id\":1000,\"name\":\"John\"}";
ObjectReader objectReader = new ObjectMapper().readerWithView(UserWithView.PublicView.class)
  .forType(UserWithView.class);
UserWithView user = objectReader.readValue(json);
assertEquals("John", user.getName());
assertNull(user.getId());

在反序列化时,需要使用 readerWithView() 而不是 writerWithView() 方法来自定义 ObjectMapper

5、使用 Mixin 动态应用 @JsonIgnore

Jackson Mixin(混合)提供了一种无需修改原始类即可应用 @JsonIgnore 注解的方法。

首先,需要定义一个接口,并在要忽略的属性上定义一个 getter 方法:

public interface PublicMixin {
    @JsonIgnore
    Long getId();
}

然后,就可以在 ObjectMapper 中将接口注册为 Mixin,从而实现所需的行为:

ObjectMapper objectMapper = new ObjectMapper().addMixIn(UserWithMixin.class, PublicMixin.class);
String result = objectMapper.writeValueAsString(user);
assertThat(result).contains("John");
assertThat(result).doesNotContain("1000");

通过动态注入的 @JsonIgnore 注解与 Mixin 配置一起使用,使得 Jackson 在反序列化对象时也会忽略字段:

String json = "{\"id\":1000,\"name\":\"John\"}";
ObjectMapper objectMapper = new ObjectMapper().addMixIn(UserWithMixin.class, UserWithMixin.PublicMixin.class);
UserWithMixin user = objectMapper.readValue(json, UserWithMixin.class);
assertNull(user.getId());
assertEquals("John", user.getName());

在这种情况下,序列化和反序列化不需要特别的自定义。只需设置正确的 Mixin 类即可。

6、各种方法的比较

从灵活性、序列化/反序列化和最佳使用案例等方面对这些方法进行比较:

方法 运行时的灵活性 用例 序列化/反序列化
@JsonFilter 运行时动态排除字段
@JsonView 在编译时定义多个序列化视图,以便在运行时动态切换
Mixin(混合) 在不修改原始类的情况下应用 @JsonIgnore

7、总结

本文介绍了在使用 Jackson 对对象进行序列化和反序列化时实现动态忽略字段的一些方法。


Ref:https://www.baeldung.com/java-jackson-fields-dynamic-ignore