1. 概述

本文将探讨如何在Jackson中实现动态字段忽略。当我们需要根据特定条件对同一对象采用不同序列化/反序列化策略时,这个技巧特别实用。我们将重点分析三种主流实现方案:@JsonFilter@JsonView和Jackson Mixins。

2. 项目准备

首先添加核心依赖(最新版本可在此处查看):

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

3. 使用 @JsonFilter 实现动态忽略

通过在类上添加注解声明过滤器:

@JsonFilter("publicFilter")
public class UserWithFilter {
    private Long id;
    private String name;
    // getters and setters
}

配置ObjectMapper注册过滤器(排除id字段):

SimpleFilterProvider filterProvider = new SimpleFilterProvider();
filterProvider.addFilter("publicFilter", SimpleBeanPropertyFilter.serializeAllExcept("id"));
ObjectMapper objectMapper = new ObjectMapper().setFilterProvider(filterProvider);

序列化测试:

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

核心优势:运行时完全动态控制字段序列化
⚠️ 踩坑提醒:反序列化时此方法无效!即使配置相同过滤器,id字段仍会被解析:

String json = "{\"id\":1000,\"name\":\"John\"}";
UserWithFilter result = objectMapper.readValue(json, UserWithFilter.class);
assertEquals(1000L, result.getId());  // id会被成功反序列化
assertEquals("John", result.getName());

4. 使用 @JsonView 实现条件忽略

通过定义视图实现编译时声明:

public class UserWithView {
    @JsonView(InternalView.class)
    private Long id;
    @JsonView(PublicView.class)
    private String name;
    // getters and setters

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

运行时切换视图(使用PublicView排除id):

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

序列化验证:

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

双向支持:反序列化同样生效,需使用readerWithView()

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());  // id被成功忽略

5. 使用 Mixins 动态应用 @JsonIgnore

定义混入接口(无需修改原类):

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

注册混入实现动态忽略:

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

双向生效:反序列化自动继承忽略规则:

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());    // id被忽略
assertEquals("John", user.getName());

6. 方案对比

方案 运行时灵活性 适用场景 序列化/反序列化
@JsonFilter 运行时动态排除字段 ❌ 仅序列化
@JsonView 编译时定义多视图运行时切换 ✅ 双向支持
Mixins 无修改原类添加@JsonIgnore ✅ 双向支持

选择建议

  • 需极致动态性 → @JsonFilter
  • 需双向支持 → @JsonView或Mixins
  • 不能修改源码 → Mixins

7. 总结

在Java这类编译型语言中实现完全动态的序列化控制并不容易,但Jackson提供了三种灵活机制:

  1. @JsonFilter:简单粗暴的运行时过滤(注意反序列化坑点)
  2. @JsonView:基于视图的条件忽略(推荐双向需求场景)
  3. Mixins:非侵入式注解注入(适合遗留系统改造)

根据实际需求选择合适方案,就能优雅解决动态字段控制问题。


原始标题:Dynamically Ignoring Fields in Jackson | Baeldung