原文鏈接:https://www.baeldung.com/jackson-serialize-field-custom-criteria
1. 概述
本教程將說明如何使用Jackson僅在字段滿足特定的自定義條件時才序列化字段。
例如,當一個整數字段是正數時才序列化它,如果不是正數則跳過。
如果你想更深入學習關於Jackson 2可以實現的其他事情,請轉到 Jackson tutorial
2. 使用Jackson Filter控制序列化處理
首先,我們使用 @JsonFilter 註解在實體類上定義一個filter:
@JsonFilter("myFilter")
public class MyDto {
private int intValue;
public MyDto() {
super();
}
public int getIntValue() {
return intValue;
}
public void setIntValue(int intValue) {
this.intValue = intValue;
}
}
然後,我們構建一個自定義的 PropertyFilter:
PropertyFilter theFilter = new SimpleBeanPropertyFilter() {
@Override
public void serializeAsField
(Object pojo, JsonGenerator jgen, SerializerProvider provider, PropertyWriter writer)
throws Exception {
if (include(writer)) {
if (!writer.getName().equals("intValue")) {
writer.serializeAsField(pojo, jgen, provider);
return;
}
int intValue = ((MyDtoWithFilter) pojo).getIntValue();
if (intValue >= 0) {
writer.serializeAsField(pojo, jgen, provider);
}
} else if (!jgen.canOmitFields()) { // since 2.3
writer.serializeAsOmittedField(pojo, jgen, provider);
}
}
@Override
protected boolean include(BeanPropertyWriter writer) {
return true;
}
@Override
protected boolean include(PropertyWriter writer) {
return true;
}
};
這個filter包含了關於intValue字段基於它的值是否將會被序列化的邏輯。
然後,將這個filter關聯到ObjectMapper上,我們就可以進行序列化操作了:
FilterProvider filters = new SimpleFilterProvider().addFilter("myFilter", theFilter);
MyDto dtoObject = new MyDto();
dtoObject.setIntValue(-1);
ObjectMapper mapper = new ObjectMapper();
String dtoAsString = mapper.writer(filters).writeValueAsString(dtoObject);
最後,我們檢查intValue字段在爲負數是確實沒有出現在JSON序列化的結果中:
assertThat(dtoAsString, not(containsString("intValue")));
3. 按條件跳過對象Skip Objects Conditionally
現在,我們來討論如何在序列化操作時基於屬性值跳過對象。我們將會跳過hidden是true的屬性:
3.1. Hidable 相關類
首先,看看我們定義的Hidden接口:
@JsonIgnoreProperties("hidden")
public interface Hidable {
boolean isHidden();
}
這裏有兩個簡單的類Person, Address實現了這個接口
Person 類:
public class Person implements Hidable {
private String name;
private Address address;
private boolean hidden;
}
Address 類:
public class Address implements Hidable {
private String city;
private String country;
private boolean hidden;
}
注意: 我們使用了 @JsonIgnoreProperties(“hidden”) 來確保 hidden 屬性不會包含在 JSON 中
3.2. 自定義Serializer
接下來,這是我們實現的自定義序列化器:
public class HidableSerializer extends JsonSerializer<Hidable> {
private JsonSerializer<Object> defaultSerializer;
public HidableSerializer(JsonSerializer<Object> serializer) {
defaultSerializer = serializer;
}
@Override
public void serialize(Hidable value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException {
if (value.isHidden())
return;
defaultSerializer.serialize(value, jgen, provider);
}
@Override
public boolean isEmpty(SerializerProvider provider, Hidable value) {
return (value == null || value.isHidden());
}
}
注意:
- 當對象不需要被跳過時,我們將將序列化操作委託給默認的序列化器。
- 我們覆寫了 isEmpty() 方法——以保證一旦有屬性是實現了Hidable接口的對象,這個屬性名將會被從JSON從排除掉
3.3. 使用BeanSerializerModifier
最後,我們使用BeanSerializerModifier在我們自定義的 HidableSerializer中注入默認的序列化器,如下:
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(Include.NON_EMPTY);
mapper.registerModule(new SimpleModule() {
@Override
public void setupModule(SetupContext context) {
super.setupModule(context);
context.addBeanSerializerModifier(new BeanSerializerModifier() {
@Override
public JsonSerializer<?> modifySerializer(
SerializationConfig config, BeanDescription desc, JsonSerializer<?> serializer) {
if (Hidable.class.isAssignableFrom(desc.getBeanClass())) {
return new HidableSerializer((JsonSerializer<Object>) serializer);
}
return serializer;
}
});
}
});
3.4. 示例輸出
這裏是一個序列化的例子:
Address ad1 = new Address("tokyo", "jp", true);
Address ad2 = new Address("london", "uk", false);
Address ad3 = new Address("ny", "usa", false);
Person p1 = new Person("john", ad1, false);
Person p2 = new Person("tom", ad2, true);
Person p3 = new Person("adam", ad3, false);
System.out.println(mapper.writeValueAsString(Arrays.asList(p1, p2, p3)));
它的輸出結果如下:
[
{
"name":"john"
},
{
"name":"adam",
"address":{
"city":"ny",
"country":"usa"
}
}
]
3.5. Test
最後,這是一些測試case。
第1個是什麼都不隱藏的case:
@Test
public void whenNotHidden_thenCorrect() throws JsonProcessingException {
Address ad = new Address("ny", "usa", false);
Person person = new Person("john", ad, false);
String result = mapper.writeValueAsString(person);
assertTrue(result.contains("name"));
assertTrue(result.contains("john"));
assertTrue(result.contains("address"));
assertTrue(result.contains("usa"));
}
第2個是隻有address字段被隱藏的case:
@Test
public void whenAddressHidden_thenCorrect() throws JsonProcessingException {
Address ad = new Address("ny", "usa", true);
Person person = new Person("john", ad, false);
String result = mapper.writeValueAsString(person);
assertTrue(result.contains("name"));
assertTrue(result.contains("john"));
assertFalse(result.contains("address"));
assertFalse(result.contains("usa"));
}
第3個是整個persion都被隱藏的case:
Now, entire person is hidden:
@Test
public void whenAllHidden_thenCorrect() throws JsonProcessingException {
Address ad = new Address("ny", "usa", false);
Person person = new Person("john", ad, true);
String result = mapper.writeValueAsString(person);
assertTrue(result.length() == 0);
}
4. 結論
這種高級過濾功能非常強大,在使用Jackson序列化複雜對象時,可以非常靈活地定製json。
一個更靈活但也更復雜的替代方案是使用完全定製的序列化器來控制JSON輸出——所以如果這個解決方案不夠靈活,可能值得研究一下。
這些例子實現的代碼片段可以在 GitHub(jackson-modules/jackson-custom-conversions)上找到,這是一個易於直接導入並運行的基於Maven的工程。
附jackson教程鏈接 https://www.baeldung.com/jackson