メインコンテンツへ移動

JSONをJavaに変換

「JSONをJavaに変換」は、サンプルのドキュメントからレコードまたはPOJOをブラウザ内で生成します。省略可能な数値フィールドはボクシングされます——intではなくIntegerです。プリミティブはnullを保持できないため、存在しないキーは0にデシリアライズされ、本物のゼロと区別がつかなくなるからです。

端末内で処理
形式
サンプルJSON
0 B1 行行 1、列 1
構文エラー · 1行1列でエラー

ドキュメントが途中で終わっています。切れている可能性があります。


^
生成されたコード読み取り専用
0 B1 行
複数のレコードがあれば貼り付けてください。一部の行にだけあるフィールドを検出し、正しい省略可能な型を生成できます。

JSONをJavaに変換はどう動きますか?

Javaのプリミティブ型は、省略可能フィールドの問題をスタイルの好みではなく具体的な問題にします。間違えると、本当に見つけにくいバグになります。

プリミティブは「存在しない」を表せない

int countは、存在しないcountキーを0にデシリアライズします。例外も警告もありません。そしてその後では、カウントが一度も送られてこなかったレコードと、カウントが本当にゼロだったレコードを区別する方法がありません。booleanはfalseに、doubleは0.0になっても同じです。そのため、省略可能なフィールドとnullになり得るフィールドはボクシングします。Integer、Long、Double、Booleanはいずれもnullを保持でき、この問いを立てられるようにします。

ジェネリクスはプリミティブを一切保持できない

List<int>はコンパイルできません。リストの要素は、省略可能かどうかに関係なくすべてボクシングされます。整数の配列がList<Integer>になるのはこのためです。

レコードかPOJOか

Java 17以降なら、レコードのほうが良いデフォルトです。不変で、ボイラープレートがなく、equals、hashCode、toStringが無料で付き、Jacksonも2.12から対応しています。privateフィールドとgetter、setterを持つPOJOが欲しくなるのは、Java 8のコードベースや、引数なしコンストラクターを要求するフレームワークを使っている場合です。どちらも用意されています。

Jacksonのアノテーションが元のキーを持つ

Javaの慣習はcamelCaseですが、JSONのキーはたいてい違います。どこか別の場所でPropertyNamingStrategyが設定されていることに頼る代わりに、すべてのコンポーネントに、ドキュメントから取った正確なキーを持つ明示的な@JsonPropertyを付けます。こうすれば、ObjectMapperの設定が何であれ、クラスは正しくバインドされます。

1ファイルにつきpublicな型は1つ

Javaでは、ソースファイルごとにpublicなトップレベル型はちょうど1つしか許されません。複数の型を生成するドキュメントは、1回の貼り付けではコンパイルできません。出力はヘッダーコメントでその旨を伝えます。各型を<TypeName>.javaとして保存するか、1つの外側のクラスの中にネストしてください。javacが拒否するファイルを、それとは書かずに出力するのは、10分を無駄にするタイプの小さな不誠実です。

サンプル

[
  { "user_id": 1, "retries": 0, "tags": ["a"] },
  { "user_id": 2, "tags": [] }
]

生成結果

public record User(
    @JsonProperty("user_id") int userId,
    @JsonProperty("retries") Integer retries,
    @JsonProperty("tags") List<String> tags
) {}

JSONをJavaに変換が対応するオプションとエッジケースは?

技術リファレンスとオプション
パラメータ型デフォルト動作とエッジケース
形状record / POJOrecordJava 17以上では、定型コードなしで不変DTOを作成するためのrecordを使用します。Java 8、または引数なしのコンストラクタとセッターが必要なものにはPOJOを使用します。
パッケージ識別子(なし)パッケージ宣言を追加します。スケッチファイルの場合は空のままにしてください。ファイルが配置されるディレクトリに一致するように設定します。
Jackson真偽値on正確なJSONキーで@JsonPropertyを出力し、バインディングが他の場所で構成されたObjectMapperの命名戦略に依存しないようにします。
Optionalをボックス化真偽値onOptionalのintはIntegerになります。これをオフにすると、キーが存在しない場合のデシリアライズ結果が0となり、実際のゼロと区別できなくなります。
stringJava型String検出されたフォーマットはInstant、LocalDateまたはUUIDにマッピングされません。Jacksonは最初の2つにはJavaTimeModuleの登録が必要であり、フォーマットが異なるレコードがある場合は例外をスローします。
integerJava型int / longサンプルのいずれかが2,147,483,647を超える場合はlongになります。JSONの数値には宣言された幅がないため、幅は値から読み取られます。
arrayJava型List<T>ジェネリックスがプリミティブを保持できないため、要素は常にボックス化されます。混合配列はList<Object>になります。
ネストしたオブジェクト型固有の型それを使用する型の前に宣言される、独自のレコードまたはクラスに昇格されます。同一の形状は、番号付きの重複を生成する代わりに1つの型を共有します。

よくある質問

あるフィールドはintで、別のフィールドはIntegerなのはなぜですか?

そのIntegerは、あるレコードには現れ、別のレコードには現れなかったからです。プリミティブのintはnullを保持できないので、Jacksonは存在しないキーを0にデシリアライズします。そしてその後の処理では、それと、本当にゼロを送ってきたレコードを区別する方法がありません。Integerならnullになれるので、欠落が見えます。全レコードに存在するフィールドはプリミティブのままです。そのほうが安価でシンプルだからです。

出力全体を1つのファイルに貼り付けても大丈夫ですか?

複数の型を生成した場合はできません。Javaではソースファイルごとにpublicなトップレベル型は1つしか許されないので、javacが拒否します。各型を<TypeName>.javaとして保存するか、ネストしている型を1つの外側のクラスの中のstaticネストクラスにしてください。どちらのケースに当たるかは、出力の冒頭に書かれています。

レコードとPOJO、どちらを使うべきですか?

Java 17以降で、オブジェクトがどこかから届いたデータであるならレコードです。不変で、equals、hashCode、toStringが正しく提供され、Jacksonも2.12からレコードに対応しています。Java 8の場合、フレームワークが引数なしコンストラクターとセッターを要求する場合(古いJPA、一部のSpringの構成)、またはオブジェクトを構築後に本当に変更する必要がある場合はPOJOです。

タイムスタンプがInstantではなくStringになるのはなぜですか?

Jacksonは、JavaTimeModuleを登録しない限りInstantにパースできず、しかも期待した書式と違う最初のレコードで例外を投げるからです。実際のAPIは、Unix秒、Unixミリ秒、オフセットありとなしのISO 8601、そしてときに地域固有の形式を送ってきます。検出された書式は上のリファレンスに注記してあります。意図して型を変更し、書式が必要とするならコンバーターを追加してください。

Lombokは必要ですか?

不要です。ジェネレーターは意図的に@Dataや@Builderを出力しません。Java 17以降では、recordがLombokの@Valueが担っていた役割を、ビルドにアノテーションプロセッサーを足さずに果たします。POJOの出力は本物のgetterとsetterを持つ素のJavaなので、どこでもコンパイルできます。プロジェクトですでにLombokを使っているなら、アクセッサーは自分で置き換えても構いませんが、このツールはそれを要求しません。

JSONはアップロードされますか?

いいえ。パース、型推論、コードの生成はすべてブラウザ内で実行されます。貼り付け中にNetworkパネルを開けば、何も外へ出ていかないのが分かります。クラスジェネレーターに貼り付けられるサンプルは、たいてい統合相手のシステムからの実際のレスポンスです。まさに、知らないサーバーを通すべきでないデータです。