Java
 Computer >> コンピューター >  >> プログラミング >> Java

Jacksonの@JsonAnyGetterと@JsonAnySetterアノテーションの使い方と活用シーン

@JsonAnyGetter / @JsonAnySetterとは

@JsonAnyGetterアノテーションを使用すると、Mapをプロパティのコンテナとして扱い、その中身をまとめてJSONにシリアライズできます。事前にフィールドとして定義していない動的なキー・バリューのペアを、JSON出力時にフラットな形で展開したい場合に非常に便利です。

一方、@JsonAnySetterアノテーションは、JSONオブジェクト内に存在するすべての未認識フィールド(Javaオブジェクト側のプロパティやセッターメソッドにマッピングされていないフィールド)に対して、同じセッターメソッドを呼び出すようJacksonに指示します。これにより、スキーマが流動的なJSONや、予期しない追加フィールドを含むJSONでも、デシリアライズ時のエラーを回避しながら柔軟にデータを受け取れます。

構文

public @interface JsonAnyGetter
public @interface JsonAnySetter

コード例

以下の例では、Employeeクラスに@JsonAnyGetterと@JsonAnySetterを適用し、Mapに格納した追加情報をJSONとして出力した後、再びオブジェクトへデシリアライズしています。

import java.io.*;
import java.util.*;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.annotation.*;

public class JsonAnyGetterAndJsonAnySetterTest {
   public static void main(String args[]) throws JsonGenerationException, JsonMappingException, IOException {
      Employee emp1 = new Employee();
      emp1.setFirstName("Adithya");
      emp1.setLastName("Sai");
      emp1.setEmpId(125);
      emp1.getAdditionalInformation().put("technology1", "Machine Learning");
      emp1.getAdditionalInformation().put("technology2", "Robotics");

      ObjectMapper mapper = new ObjectMapper();
      String jsonStr = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(emp1);
      System.out.println(jsonStr);

      System.out.println("Deserializing JSON to Object:");
      Employee emp2 = mapper.readValue(jsonStr, Employee.class);
      System.out.println("id : " + emp2.getEmpId());
      System.out.println("firstName : " + emp2.getFirstName());
      System.out.println("lastName : " + emp2.getLastName());
      System.out.println("Additional information : " + emp2.getAdditionalInformation());
   }
}

// Employeeクラス
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({"firstName", "lastName", "technologies", "empId"})
class Employee {
   @JsonProperty("EMPLOYEE_ID")
   private int empId;

   @JsonProperty("EMPLOYEE_FIRST_NAME")
   private String firstName;

   @JsonProperty("EMPLOYEE_LAST_NAME")
   private String lastName;

   private Map<String, String> additionalInformation = new HashMap<>();

   public int getEmpId() {
      return empId;
   }
   public void setEmpId(int empId) {
      this.empId = empId;
   }
   public String getFirstName() {
      return firstName;
   }
   public void setFirstName(String firstName) {
      this.firstName = firstName;
   }
   public String getLastName() {
      return lastName;
   }
   public void setLastName(String lastName) {
      this.lastName = lastName;
   }

   @JsonAnyGetter
   public Map<String, String> getAdditionalInformation() {
      return additionalInformation;
   }
   public void setAdditionalInformation(Map<String, String> additionalInformation) {
      this.additionalInformation = additionalInformation;
   }

   @JsonAnySetter
   public void setAdditionalProperty(final String name, final String value) {
      this.additionalInformation.put(name, value);
   }
}

実行結果

{
  "EMPLOYEE_FIRST_NAME" : "Adithya",
  "EMPLOYEE_LAST_NAME" : "Sai",
  "EMPLOYEE_ID" : 125,
  "technology1" : "Machine Learning",
  "technology2" : "Robotics"
}
Deserializing JSON to Object:
id : 125
firstName : Adithya
lastName : Sai
Additional information : {technology1=Machine Learning, technology2=Robotics}

ポイント解説

シリアライズ時には、@JsonAnyGetterが付けられたgetAdditionalInformation()メソッドから返されるMapのエントリ(technology1、technology2)が、他の通常のプロパティと同じ階層に展開されて出力されていることが分かります。Mapの中身が独立したネスト構造ではなく、JSONのトップレベルに並んでいる点が特徴です。

デシリアライズ時には、JSON内の「technology1」「technology2」はEmployeeクラスの明示的なプロパティではないため、未認識フィールドとして扱われ、@JsonAnySetterが付けられたsetAdditionalProperty()メソッドが自動的に呼び出されます。その結果、これらの値がadditionalInformationマップに格納され直します。

このように、外部APIのレスポンスなど、将来的にフィールドが増える可能性があるJSONや、拡張可能なメタデータを持つオブジェクトを扱う場合に、@JsonAnyGetterと@JsonAnySetterの組み合わせは非常に有効です。なお、未知のフィールドを単純に無視したいだけであれば、@JsonIgnoreProperties(ignoreUnknown = true)を使うという選択肢もあります。

  1. Javaのpack()メソッドとは?使い方とsetSize()との違いを解説

    pack()メソッドは、JavaのWindowクラスで定義されているメソッドで、フレーム内のすべてのコンポーネントがそれぞれの推奨サイズ(preferred size)以上で表示されるように、ウィンドウのサイズを自動的に調整します。 代替手段として、setSize()メソッドやsetBounds()メソッドを呼び出して、フレームのサイズを明示的に指定する方法もあります。しかし一般的には、pack()メソッドを使用する方がsetSize()よりも推奨されています。その理由は、pack()がフレームのサイズ決定をレイアウトマネージャーに委ねることができ、レイアウトマネージャーはプラットフォーム間の

  2. JavaのSwingUtilities.invokeLater()メソッドの使い方を徹底解説

    invokeLater()メソッドとはinvokeLater()メソッドは、SwingUtilitiesクラスが提供するstaticメソッドの一つで、AWTイベントディスパッチスレッド(Event Dispatch Thread)上でタスクを非同期に実行するために使用されます。このメソッドはSwingUtilities.invokeAndWait()とよく似た動作をしますが、大きな違いは、処理のリクエストをイベントキューに登録した後、すぐに呼び出し元へ制御を返すという点です。つまり、invokeLater()は引数で渡されたRunnableオブジェクト内のコードブロックの実行完了を待ちません。