1. 概述
本教程將演示如何使用 Jackson僅在滿足特定自定義標準時才序列化字段。
例如,假設我們只想在整數值為正數時才序列化它,並且如果它不是正數,則完全跳過它。
如果您想更深入地瞭解 Jackson 2可以執行的其他酷方法 – 請訪問主 Jackson 教程。
2. 使用 Jackson Filter 控制序列化過程
首先,我們需要在實體上定義過濾器,使用 @JsonFilter 註解:@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;
}
};
此過濾器包含實際的邏輯,決定 intValue 字段是否將被序列化,基於其值。
接下來,我們將此過濾器連接到 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
Now – let’s discuss how to skip objects while serializing based on property
3.1. Hidable Classes
First, let’s take a look at our
@JsonIgnoreProperties("hidden")
public interface Hidable {
boolean isHidden();
}
And we have two simple classes implementing this interface
public class Person implements Hidable {
private String name;
private Address address;
private boolean hidden;
}
And
public class Address implements Hidable {
private String city;
private String country;
private boolean hidden;
}
Note: We used
3.2. Custom Serializer
Next – here is our custom 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());
}
}
Note that:
- When the object will not be skipped, we delegate the serialization to the default injected serializer.
- We overridden the method
isEmpty() – to make sure that in case of Hidable object is a property, property name is also excluded from JSON.
3.3. Using BeanSerializerModifier
Finally, we will need to use
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. Sample Output
Here is a simple serialization example:
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)));
And the output is:
[
{
"name":"john"
},
{
"name":"adam",
"address":{
"city":"ny",
"country":"usa"
}
}
]
3.5. Test
Finally – here is few test cases:
First 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"));
}
Next,
@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"));
}
Now,
@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 輸出——如果這個解決方案不夠靈活,則可能需要考慮一下。