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

1概要

この記事では、フィールドが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つのフィールドのうち、publicの 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ゲッターが非パブリックフィールドをシリアライズ可能およびデシリアライズ可能にする

**

さて、フィールド、特に非公開フィールドを直列化可能にするもう1つの簡単な方法は、それにゲッターを追加することです。

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

    public String getStringValue() {
        return stringValue;
    }
}

stringValue フィールドは直列化可能であることが期待されていますが、もう一方のプライベートフィールドはgetterを持たないため、そうではありません。

@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すべてのフィールドをグローバルに直列化可能にする

たとえば、実際にはソースコードを直接変更できない場合もあります - Jacksonが外部からの非パブリックフィールドを処理する方法を設定する必要があります。

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. シリアライゼーション/デシリアライゼーション でプロパティの名前を変更する

どのフィールドがシリアライズまたはデシリアライズされるかを制御するだけでなく、フィールドをJSONにマッピングする方法やその逆の方法を制御することもできます。

私はリンクをカバーしました:/jackson-name-of-property[この設定はここ]。

7. シリアライゼーションまたはデシリアライゼーションのフィールドを無視

次のリンク:/jackson-ignore-properties-on-serialization[このチュートリアル]では、フィールドをシリアル化と逆シリアル化で完全に無視する方法についてのガイドを提供しています。

しかし、時にはどちらか一方だけを無視し、両方を無視する必要がある場合もあります。 Jacksonは、この興味深いユースケースにも対応できるだけの柔軟性を持っています。

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

そこに到達するには、単に password のゲッターに @ JsonIgnore アノテーションを追加し、setterに @ 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がどのようにして** どのフィールドをシリアライズ/デシリアライズし、どのフィールドを無視するか、そしてもちろんそれを完全に制御する方法について説明します。

また、ジャクソン2を理解するための次のステップに進むには、 フィールドを無視する デシリアライズ などの記事を読んでください。 JSON配列からJava配列またはコレクションへの変換 ]。

これらすべての例とコードスニペットの実装は my github project にあります。そのままインポートして実行するのは簡単です。