[翻译]Serialize Only Fields that meet a Custom Criteria with Jackson(使用Jackson进行JSON序列化实现按条件对字段进行序列化)

原文链接: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

现在,我们来讨论如何在序列化操作时基于属性值跳过对象。我们将会跳过hiddentrue的属性:

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

posted @ 2021-11-14 18:29  liqipeng  阅读(283)  评论(0编辑  收藏  举报