ホーム › ファイルI/O › F-04: JSON の読み書き
F-04: JSON の読み書き(Jackson)
REST API やファイルで JSON を扱う場面は現代の Java 開発で頻繁に出てきます。 Pure Java には標準の JSON ライブラリがないため、実務では Jackson や Gson などの外部ライブラリを使うのが一般的です。 ここでは最も普及している Jackson を使ったパターンを解説します。
Jackson の基本概念
Jackson はの主要な処理方法は2つです。
データバインディング(POJO / record との相互変換)
Java オブジェクトと JSON を直接変換する方法です。クラス定義が必要ですが、型安全に扱えます。writeValueAsString() でシリアライズ、readValue() でデシリアライズします。
ツリーモデル(JsonNode)
JSON をツリー構造として扱う方法です。クラス定義なしで任意のフィールドにアクセスできます。 スキーマが不定な JSON や、一部のフィールドだけ取り出したい場合に便利です。
Maven 依存定義
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.0</version>
</dependency>サンプルコード
よくあるミス・注意点
⚠️ ObjectMapper は new するたびに重い処理が走る
ObjectMapper はスレッドセーフかつ生成コストが高いオブジェクトです。 メソッドを呼ぶたびに new ObjectMapper() すると、 大量のリクエストを処理するアプリでは深刻なパフォーマンス問題になります。 必ず static final で1度だけ生成して使い回しましょう。
// NG: メソッドを呼ぶたびに新規作成(パフォーマンスが悪い)
public String toJson(Object obj) throws Exception {
return new ObjectMapper().writeValueAsString(obj);
}
// OK: static final で1つだけ生成
private static final ObjectMapper MAPPER = new ObjectMapper();
public String toJson(Object obj) throws Exception {
return MAPPER.writeValueAsString(obj);
}📌 POJO にはデフォルトコンストラクタが必要
Jackson はデシリアライズ時にデフォルトコンストラクタ(引数なしのコンストラクタ)を使ってオブジェクトを生成します。 引数付きのコンストラクタしか定義していないクラスに readValue() するとInvalidDefinitionException が発生します。
📌 未知フィールドがあるとデシリアライズが失敗する
デフォルトでは JSON に POJO にないフィールドがあると UnrecognizedPropertyException が発生します。 API レスポンスが変わっても壊れないようにするには @JsonIgnoreProperties(ignoreUnknown = true) を付けましょう。
テストする観点
- シリアライズしてデシリアライズすると元のオブジェクトと一致するか(ラウンドトリップテスト)
- JSON に未知フィールドが含まれているとき、
@JsonIgnorePropertiesなしで例外が発生するか - 不正な JSON 文字列を渡すと
JsonParseExceptionがスローされるか - 日本語(マルチバイト文字)を含む値が文字化けなく扱えるか
- 配列 JSON → オブジェクト配列への変換が正しく機能するか
JsonNode.path()で存在しないキーにアクセスしても例外が発生しないか