Jacksonとカスタム基準を満たすフィールドのみを直列化する

1概要

このチュートリアルでは、特定のカスタム基準を満たしている場合にのみ、Jacksonを使用してフィールドをシリアル化する方法を説明します。

たとえば、正の値の場合にのみ整数値をシリアル化したいとし、そうでない場合は完全にスキップしたいとします。

あなたがもっと深く掘り下げて あなたがジャクソン2 ですることができる** 他のクールなことを学びたいなら - メインのジャクソンチュートリアル に向かって進んでください。

2 Jacksonフィルタを使用してシリアル化プロセスを制御する

まず、 @ 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条件付きでオブジェクトをスキップする

それでは、プロパティ value に基づいてシリアル化しながらオブジェクトをスキップする方法について説明しましょう。プロパティ hidden ​​が true であるすべてのオブジェクトをスキップします。

3.1. 隠しクラス

まず、私たちの Hidable インターフェースを見てみましょう。

@JsonIgnoreProperties("hidden")
public interface Hidable {
    boolean isHidden();
}

そしてこのインターフェースを実装する2つの単純なクラス Person Address があります。

個人 クラス:

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;
}

注: hidden ​​プロパティ自体がJSONに含まれないようにするために、 @ JsonIgnoreProperties(“ hidden”) を使用しました

3.2. カスタムシリアライザ

次は - これが私たちのカスタムシリアライザです。

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() - をオーバーライドして、

隠しオブジェクトはプロパティです。プロパティ名も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
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"));
}

次に、 アドレスのみが隠されています :

@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"));
}

今、 人全体が隠されている :

@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出力を制御するために完全にカスタム化されたシリアライザを使用することです。

これらすべての例とコードスニペットの実装は、https://github.com/eugenp/tutorials/tree/master/jackson#readme にあります。 - これはEclipseベースのプロジェクトです。そのままインポートして実行するのは簡単です。