Jackson 中的動態忽略字段
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:
註解一個class
:
@JsonFilter("publicFilter")
public class UserWithFilter {
private Long id;
private String name;
// getters and setters
}
我們可以動態配置ObjectMapper
並註冊一個[PropertyFilter](https://www.javadoc.io/doc/com.fasterxml.jackson.core/jackson-databind/latest/index.html)
來序列化除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
進行有條件忽略
@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();
}
然後我們可以將介面註冊為ObjectMapper
中的 Mixin,並實作所需的行為:
ObjectMapper objectMapper = new ObjectMapper().addMixIn(UserWithMixin.class, PublicMixin.class);
String result = objectMapper.writeValueAsString(user);
assertThat(result).contains("John");
assertThat(result).doesNotContain("1000");
使用 mixin 配置動態注入的@JsonIgnore
註解使 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 提供了幾種機制來自訂序列化行為並根據不同的運行時需求調整輸出。
上述範例的程式碼可在 GitHub 上找到。