1. 簡介
在本教程中,我們將學習如何動態忽略 Jackson 中的字段。
這在我們需要根據特定條件對同一對象進行不同的序列化和反序列化時非常有用。
我們將探索三種實現這一目標的方法:@JsonFilter, @JsonView,以及 Jackson Mixins。
2. 項目設置
讓我們通過添加所需的 Jackson 庫開始我們的教程:<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.2</version>
</dependency>
最新版本可以在這裏找到 這裏。
3. 使用 @JsonFilter 動態忽略
我們首先要研究的方法是通過註解來指定在序列化過程中使用的過濾器。
通過對一個 類 進行註解,使用 @JsonFilter,
@JsonFilter("publicFilter")
public class UserWithFilter {
private Long id;
private String name;
// getters and setters
}
我們可以動態配置 ObjectMapper 並註冊一個 PropertyFilter 以序列化所有字段,除了 id:
SimpleFilterProvider filterProvider = new SimpleFilterProvider();
filterProvider.addFilter("publicFilter", SimpleBeanPropertyFilter.serializeAllExcept("id"));
ObjectMapper objectMapper = new ObjectMapper().setFilterProvider(filterProvider);
然後我們可以序列化 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 是一種通過定義不同視圖來控制字段可見性的方法。與之前的做法不同,我們需要在編譯時定義序列化選項:
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 {}
}
在運行時,我們可以通過配置 ObjectMapper 來選擇用於序列化對象的正確的 JsonView:
ObjectWriter objectWriter = new ObjectMapper().writerWithView(UserWithView.PublicView.class);
在這種情況下,結果將不包含 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. 使用 Mixins 動態應用 @JsonIgnore 標註
Jackson Mixins 提供了一種在不修改原始類的情況下 應用 @JsonIgnore 標註的方法。我們需要定義一個接口,該接口包含要忽略的屬性的 getter 方法:
public interface PublicMixin {
@JsonIgnore
Long getId();
}
然後,我們可以將該接口作為 Mixin 在 ObjectMapper 中註冊,從而實現所需的功能:
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 | 中 | 定義多個編譯時序列化視圖,以便在運行時動態切換 | 是 |
| Mixins | 中 | 在不修改原始類的情況下應用@JsonIgnore | 是 |
這張表總結了每種方法的優勢和權衡。主要決策取決於我們是否需要反序列化工作以及序列化,是否需要高運行時靈活性,或者我們是否不會編輯現有源代碼。
7. 結論
在本文中,我們探討了使用 Jackson 動態序列化和反序列化對象的幾種方法。對於像 Java 這樣編譯型語言而言,實現完全的運行時動態性非常困難。然而,Jackson 提供了多種機制來定製序列化行為,並根據不同的運行時需求調整輸出。