ジャクソン - どのフィールドをシリアライズ/デシリアライズするかを決める

ジャクソン–シリアル化/非シリアル化するフィールドを決定する

1. 概要

この記事では、if a field is serialized / deserialized by Jacksonを制御するかどうかを制御するさまざまな方法について説明します。

2. パブリックフィールド

フィールドをシリアル化および非シリアル化できるようにする最も簡単な方法は、フィールドをパブリックにすることです。

public、package-private、privateで単純なクラスを宣言しましょう

public class MyDtoAccessLevel {
    private String stringValue;
    int intValue;
    protected float floatValue;
    public boolean booleanValue;
    // NO setters or getters
}

クラスの4つのフィールドのうち、パブリックbooleanValueのみがデフォルトでJSONにシリアル化されます。

@Test
public void givenDifferentAccessLevels_whenPublic_thenSerializable()
  throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();

    MyDtoAccessLevel dtoObject = new MyDtoAccessLevel();

    String dtoAsString = mapper.writeValueAsString(dtoObject);
    assertThat(dtoAsString, not(containsString("stringValue")));
    assertThat(dtoAsString, not(containsString("intValue")));
    assertThat(dtoAsString, not(containsString("floatValue")));

    assertThat(dtoAsString, containsString("booleanValue"));
}

3. ゲッターは非パブリックフィールドをシリアライズ可能およびデシリアライズ可能にします

ここで、フィールド(特に非パブリックフィールド)をシリアライズ可能にする別の簡単な方法は、ゲッターを追加することです。

public class MyDtoWithGetter {
    private String stringValue;
    private int intValue;

    public String getStringValue() {
        return stringValue;
    }
}

stringValueフィールドはシリアライズ可能であると予想されますが、他のプライベートフィールドはゲッターがないため、シリアライズ可能ではありません。

@Test
public void givenDifferentAccessLevels_whenGetterAdded_thenSerializable()
  throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();

    MyDtoGetter dtoObject = new MyDtoGetter();

    String dtoAsString = mapper.writeValueAsString(dtoObject);
    assertThat(dtoAsString, containsString("stringValue"));
    assertThat(dtoAsString, not(containsString("intValue")));
}

直感に反して、ゲッターはプライベートフィールドもデシリアライズ可能にします。ゲッターを取得すると、フィールドはプロパティと見なされるためです。

それがどのように機能するかを見てみましょう。

@Test
public void givenDifferentAccessLevels_whenGetterAdded_thenDeserializable()
  throws JsonProcessingException, JsonMappingException, IOException {
    String jsonAsString = "{\"stringValue\":\"dtoString\"}";
    ObjectMapper mapper = new ObjectMapper();
    MyDtoWithGetter dtoObject = mapper.readValue(jsonAsString, MyDtoWithGetter.class);

    assertThat(dtoObject.getStringValue(), equalTo("dtoString"));
}

4. セッターは非公開フィールドのみを逆シリアル化可能にします

ゲッターがどのようにしてプライベートフィールドをシリアル化可能およびデシリアライズ可能にするかを見ました。 一方、セッターは非パブリックフィールドのみをデシリアライズ可能としてマークします。

public class MyDtoWithSetter {
    private int intValue;

    public void setIntValue(int intValue) {
        this.intValue = intValue;
    }

    public int accessIntValue() {
        return intValue;
    }
}

ご覧のとおり、今回はプライベートintValueフィールドにセッターしかありません。 値にアクセスする方法はありますが、それは標準的なゲッターではありません。

intValueのマーシャリング解除プロセスは正しく機能するはずです。

@Test
public void givenDifferentAccessLevels_whenSetterAdded_thenDeserializable()
  throws JsonProcessingException, JsonMappingException, IOException {
    String jsonAsString = "{\"intValue\":1}";
    ObjectMapper mapper = new ObjectMapper();

    MyDtoSetter dtoObject = mapper.readValue(jsonAsString, MyDtoSetter.class);

    assertThat(dtoObject.anotherGetIntValue(), equalTo(1));
}

そして、前述したように、セッターはフィールドを非シリアル化するだけで、シリアル化することはできません:

@Test
public void givenDifferentAccessLevels_whenSetterAdded_thenStillNotSerializable()
  throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();

    MyDtoSetter dtoObject = new MyDtoSetter();

    String dtoAsString = mapper.writeValueAsString(dtoObject);
    assertThat(dtoAsString, not(containsString("intValue")));
}

5. すべてのフィールドをグローバルにシリアル化可能にする

たとえば、実際にソースコードを直接変更できない場合があります。ジャクソンが外部からの非公開フィールドを処理する方法を構成する必要があります。

この種のグローバル構成は、AutoDetect関数をオンにして、シリアル化にpublicフィールドまたはgetter / setterメソッドを使用するか、すべてのフィールドのシリアル化をオンにすることで、ObjectMapperレベルで実行できます。

ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, Visibility.NONE);
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);

次のテストケースは、MyDtoAccessLevelのすべてのメンバーフィールド(非公開を含む)がシリアル化可能であることを確認します。

@Test
public void givenDifferentAccessLevels_whenSetVisibility_thenSerializable()
  throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();
    mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);

    MyDtoAccessLevel dtoObject = new MyDtoAccessLevel();

    String dtoAsString = mapper.writeValueAsString(dtoObject);
    assertThat(dtoAsString, containsString("stringValue"));
    assertThat(dtoAsString, containsString("intValue"));
    assertThat(dtoAsString, containsString("booleanValue"));
}

6. シリアル化/逆シリアル化でプロパティの名前を変更する

どのフィールドをシリアル化または逆シリアル化するかを制御するだけでなく、have control over the way a fields maps to JSON and backも実行できます。 this configuration hereをカバーしました。

7. シリアル化または逆シリアル化のフィールドを無視する

this tutorialに続いて、シリアル化と逆シリアル化でフィールドを完全に無視する方法のガイドがあります。

ただし、どちらか一方のフィールドのみを無視し、両方のフィールドを無視する必要がない場合もあります。 ジャクソンは、この興味深いユースケースにも対応できる柔軟性を備えています。

次の例は、JSONにシリアル化してはならない機密のパスワード情報を含むUserオブジェクトを示しています。

そこに到達するには、passwordのゲッターに@JsonIgnoreアノテーションを追加し、セッターに@JsonPropertyアノテーションを適用してフィールドの逆シリアル化を有効にします。

@JsonIgnore
public String getPassword() {
    return password;
}
@JsonProperty
public void setPassword(String password) {
    this.password = password;
}

これで、パスワード情報はJSONにシリアル化されなくなります。

@Test
public void givenFieldTypeIsIgnoredOnlyAtSerialization_whenUserIsSerialized_thenIgnored()
  throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();

    User userObject = new User();
    userObject.setPassword("thePassword");

    String userAsString = mapper.writeValueAsString(userObject);
    assertThat(userAsString, not(containsString("password")));
    assertThat(userAsString, not(containsString("thePassword")));
}

ただし、パスワードを含むJSONは、Userオブジェクトに正常に逆シリアル化されます。

@Test
public void givenFieldTypeIsIgnoredOnlyAtSerialization_whenUserIsDeserialized_thenCorrect()
  throws JsonParseException, JsonMappingException, IOException {
    String jsonAsString = "{\"password\":\"thePassword\"}";
    ObjectMapper mapper = new ObjectMapper();

    User userObject = mapper.readValue(jsonAsString, User.class);

    assertThat(userObject.getPassword(), equalTo("thePassword"));
}

8. 結論

このチュートリアルでは、Jacksonがプロセスでwhich field is serialized/deserialized and which gets ignoredを選択する方法の基本と、もちろんそれを完全に制御する方法について説明します。

また、ignoring a fielddeserializing a JSON Array to a Java Array or Collectionなどの記事を深く掘り下げて、Jackson2を理解するための次のステップに進むこともできます。

これらすべての例とコードスニペットの実装はmy github projectにあります。これはEclipseベースのプロジェクトであるため、そのままインポートして実行するのは簡単です。