Map<String, Object> で動的な JSON キーをモデリングする
JSON が任意のキー(フィーチャーフラグ、メタデータ、動的設定など)を持つ場合は、クラスを生成せず Map<String, Object> としてモデリングします。
詳細な説明
POJO が向かないケース
ほとんどの JSON は固定スキーマ — キーは事前にわかっており、各キーに文書化された意味があります。一方でフィーチャーフラグ名、ユーザー定義メタデータ、任意タグのように、キー自体がデータである JSON も存在します。そうしたケースでは、各キーを Java フィールドにモデリングするのは不可能です。代わりに Map<String, Object> を使います。
動的キーを含む JSON の例
{
"user_id": 42,
"feature_flags": {
"new_dashboard": true,
"beta_search": false,
"ai_assistant": true
}
}
推奨される Java モデル
package com.example.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Map;
public class UserContext {
@JsonProperty("user_id")
private Integer userId;
@JsonProperty("feature_flags")
private Map<String, Boolean> featureFlags;
// アクセサ
}
値の型が揃っているなら(ここではすべて boolean)型安全のため Map<String, Boolean> を、混在しているなら Map<String, Object> を使います。
ジェネレーターが自動判定できない理由
ジェネレーターは JSON オブジェクトを見てクラスを生成します。キーがスキーマの一部(firstName、lastName)なのかデータの一部(new_dashboard、beta_search)なのかはわかりません。ドメイン知識でキーが動的だとわかっている場合は、生成されたクラスを Map フィールドに置き換えてください。
「Map 形状」の JSON を見分けるヒューリスティック
- キーがユーザーが作成しそうな識別子(UUID、スラッグ、自由テキスト)に見える
- 値がすべて同じプリミティブ型
- ドキュメント間でキーの数が変わる
- ドキュメントが「メタデータ」「タグ」「カスタムフィールド」と説明している
Jackson の挙動
Jackson は Map<String, T> をネイティブに扱うため、フィールド型以外のアノテーションは不要です。デシリアライザは各 JSON キーを Map エントリとして読み込み、値を T にデコードします。
スキーマと Map の混在
{
"id": 1,
"name": "Acme",
"tags": ["startup", "fintech"],
"metadata": {
"founded_year": 2018,
"headcount": 42,
"location": "Berlin"
}
}
public class Company {
private Integer id;
private String name;
private List<String> tags;
private Map<String, Object> metadata; // 形状不定
}
既知のスキーマ + 拡張用のオープンな metadata バッグ、というよくあるパターンです。
ユースケース
フィーチャーフラグシステム、ユーザー設定、CRM コンタクトのカスタム属性、拡張可能なメタデータを持つ Webhook イベントペイロードなどでは、生成クラスより Map<String, Object> フィールドが向いています。