概要

REST API との連携、設定ファイルの読み書き、ログ出力のフォーマットなど、JSON を扱う場面は業務システムでも増え続けています。Java 標準 API には JSON パーサーが含まれていないため、多くのプロジェクトでは Jackson を使うことになります。ObjectMapper の基本的な使い方は簡単ですが、record との組み合わせ、ネスト構造の安全なアクセス、未知フィールドへの対処など、実務で迷いやすいポイントが散在しています。この記事では、Jackson の ObjectMapper を軸に、シリアライズ・デシリアライズの基本パターンからツリー操作、配列やリストの変換までを整理します。

使いどころ

外部 API のレスポンス JSON を Java オブジェクトにマッピングして業務ロジックで使う

DB から取得したデータを JSON 形式に変換してフロントエンドに返す

設定ファイルやテストデータを JSON 形式で管理し、起動時やテスト時に読み込む

コード例

Jackson による JSON シリアライズ・デシリアライズ
import com.fasterxml.jackson.annotation.JsonProperty;

public class JsonParsing {

    record Person(
        @JsonProperty("name") String name,
        @JsonProperty("age") int age
    ) {}

    private static final ObjectMapper MAPPER = new ObjectMapper();

    public static void main(String[] args) throws Exception {

        var person = new Person("山田太郎", 30);
        var json = MAPPER.writeValueAsString(person);
        System.out.println("JSON: " + json);

        var input = """
                {"name":"鈴木花子","age":25}
                """;
        var parsed = MAPPER.readValue(input, Person.class);
        System.out.println("オブジェクト: " + parsed);

        var nested = """
                {"id":1,"address":{"city":"Tokyo","zip":"100-0001"}}
                """;
        JsonNode root = MAPPER.readTree(nested);
        System.out.println("city: " + root.path("address").path("city").asText());

        var arrayJson = """
                [{"name":"田中","age":20},{"name":"佐藤","age":35}]
                """;
        var people = List.of(MAPPER.readValue(arrayJson, Person[].class));
        people.forEach(p -> System.out.println("  " + p));
    }
}

Java 8 / 17 / 21 の完全なサンプルコードは GitHub リポジトリ で確認できます。

Version Coverage

record(Java 16+)で不変なデータクラスとしてマッピングできる。テキストブロックで JSON リテラルを読みやすく記述できる。

Java 17
// Java 17: record で簡潔にマッピング
record Person(
    @JsonProperty("name") String name,
    @JsonProperty("age") int age
) {}
var person = MAPPER.readValue(json, Person.class);

var input = """
    {"name":"山田","age":30}
    """;

Library Comparison

JacksonJava の JSON ライブラリのデファクト。Spring Boot にもバンドルされており、多くのプロジェクトで標準的に使われる。依存サイズがやや大きい。設定項目が多く、初回学習コストがある。
Gson軽量な JSON ライブラリが欲しい場合。アノテーションなしでも動作する。record サポートは追加設定が必要。大規模プロジェクトでは Jackson のほうが機能が豊富。
JSON-P / JSON-B(Jakarta EE)Jakarta EE 環境で標準仕様に準拠したい場合。スタンドアロンで使うには依存の追加が必要。Jackson ほどのエコシステムはない。

注意点

ObjectMapper はスレッドセーフだがインスタンス生成コストが高い。static final で1つだけ作成し、メソッドごとに new しないこと。

デシリアライズ対象クラスにはデフォルトコンストラクタが必要(POJO の場合)。record の場合は @JsonProperty でフィールド名を明示する。

未知のフィールドがあると UnrecognizedPropertyException が発生する。@JsonIgnoreProperties(ignoreUnknown = true) で回避可能。API レスポンスを受ける場合は設定推奨。

JsonNode.get() は存在しないキーで null を返すため NPE のリスクがある。path() を使えば MissingNode が返り安全。

FAQ

ObjectMapper を毎回 new してはいけないのですか。

インスタンス生成コストが高いため、static final で1つだけ作成してください。ObjectMapper はスレッドセーフです。

record クラスで Jackson を使うにはどうしますか。

Jackson 2.12 以降で record をサポートしています。@JsonProperty でフィールド名を明示するのが確実です。

ネスト構造の JSON で特定の値だけ取りたい場合は。

ObjectMapper.readTree() で JsonNode を取得し、path("key").path("nested").asText() のようにチェーンで辿れます。

関連書籍

この記事のテーマをさらに深く学びたい方へ。

※ Amazon アソシエイトリンクを含みます