2026/07/24 1:01
JEP 540:簡易 JSON API(インキュベーターへの登録完了)
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
jdk.incubator.json API は、RFC 8259 に厳密に準拠する JSON の解析および生成のための、軽量な Java 組み込みソリューションを提供し、外部ライブラリの使用を不要とします。この新しい標準は、ストリーミングやデータバインディングなどの高度な機能よりも単純さ、堅牢性、使いやすさを優先することで、旧来の JEP 198 の提案に取って代わります。その設計思想は、JsonValue というシールドなインターフェースと、それ専用のサブインターフェース(例:JsonString、JsonNumber など)を中心に据えており、これにより開発者は面倒なダウンキャスト操作を行うことなくデータをアクセスできます。API は厳格な型変換および「early failure(fail-fast)」動作を強制し、無効な構造を即座に拒否できるようにしつつ、オプションメンバー向けの安全なメソッド tryGet() も提供します。インキュベイト・モジュールとしてこの機能はデフォルトで無効化されており、コンパイルおよび実行時に --add-modules フラグを使用して明示的に有効化する必要があります。現時点では BigDecimal などの複合型には対応していませんが、今後のアップデートでは損失のない精度の追加を目標としながら、その核心的な目標であるマシン対マシンの通信における厳密な準拠性を維持する予定です。本文
JDK インキュベーター: JSON API (jdk.incubator.json)
要約
- 標準的な API:外部ライブラリの依存関係を不要とし、JSON の解析と生成にシンプルで標準的な API を提供します。
- 簡素な実装:少量のコードで多くの JSON 処理タスクを完了できます。
- 実験的機能:本 API はインキュベーター(実験的)API です。
沿革
- 2014 年発表の「軽量 JSON API」(JEP 198)の後継です。
- 環境の変化に伴い、より異なるアプローチを採用しています。
目的と非目的
目的
- RFC 8259 準拠:Java プラットフォーム上で、低コスト(簡略化された手順)で RFC 8259 に準拠する JSON ドキュメントを処理します。
- シンプルさの維持:API は小さく、学習が容易であり、機械間通信に必要なデータ型と操作のみを提供します。
- 解析設定の複数化や構文拡張、データバインド(結合)、ストリーミングなどの機能は含まれません。
- 探索的プログラミング:明確なエラーメッセージと共に素早く失敗するメソッドを提供し、不慣れな JSON ドキュメントへの迅速な探索を可能にします。
- コードは事実上のスキーマとして機能し、構造の進化に対してレジリエント(回復力)です。
- JDK 内蔵能力:JDK が標準的に JSON を解析・生成できる能力を持ちます。
非目的
- 確立された外部 JSON ライブラリの置換ではありません。
動機
JSON は現代コンピューティングで不可欠ですが、Java エコシステムには Jackson や Gson など多様なライブラリが存在します。しかし、「JSON ドキュメントから一部のデータを抽出する」という単純なタスクにおいて、Java コードも Python や Go のようにシンプルであるべきです。
- 最近の進化への貢献:コレクション用のファクトリーメソッドや
宣言などと同様、単純なタスクを実現しやすくすることを目標としています。var - 設定ファイルとしての JSON:JDK は現在プロパティ形式を設定ファイルに使用していますが、構造化データ(アレイ)の表現には工夫が必要でした。JSON を組み込むと、以下のように自然なアレイ表現が可能になります。
// プロパティ形式の限界(巧妙な回避策が必要) security.provider.1=SUN security.provider.2=SunRsaSign security.provider.3=SunEC // JSON の利点(自然な表現) { "providers": [ "SUN", "SunRsaSign", "SunEC" ], ... }
概要
jdk.incubator.json API は、JSON 値を表す JsonValue インターフェースを核としています。
JSON のプリミティブタイプ
- 文字列:
"Hello" - 数値:基数 10 の十進数字(例:
,6
)2.9E+5 - ブール値:
/truefalse - null:
null
JSON の構造体タイプ
- オブジェクト:
で囲まれ、メンバー(鍵と値)の集合です。{ } - アレイ:
で囲まれ、JSON 値の順序付きリストです。[ ]
これらに対応する
JsonValue のサブインターフェースは以下の 6 つです:
,JsonString
,JsonNumber
,JsonBoolean
(プリミティブ)JsonNull
,JsonObject
(構造体)JsonArray
JsonValue は**シールドされた(sealed)**ため、型スイッチ文のデフォルト処理が必須となります。
解析と生成の実例
解析(抽出)
米国国家気象庁 API のレスポンスから予報気温の平均を計算する例:
String body = ... REST レスポンシボディ(JSON ドキュメント)... ; JsonValue json = Json.parse(body); json.get("properties").get("periods").asList().stream() .mapToInt(j -> j.get("temperature").asInt()) .average() .ifPresent(IO::println);
生成(出力)
JSON ドキュメントを生成する例:
IO.println(JsonObject.of(Map.of("providers", JsonArray.of(List.of( JsonString.of("SUN"), JsonString.of("SunRsaSign"), JsonString.of("SunEC") )))));
出力:
{"providers":["SUN","SunRsaSign","SunEC"]}
JSON ドキュメントの解析とナビゲーション
Json.parse(...) メソッドを呼び出すだけで、JSON ドキュメント(文字列や char[])が名前付きツリーとして返されます。
- 厳格な解析:RFC 8259 に準拠し、末尾のカンマやコメントなどの構文拡張はサポートされません。重複するメンバー名もエラーとします。
- 例外: 解析失敗時は
をスローします(エラー詳細と位置情報付き)。unchecked JsonParseException - アクセスメソッド:
: メンバー値を取得(型キャスト不要)。get(String)
: アレイ要素を取得。get(int)
不適切なタイプや存在しないメンバーへのアクセスは
JsonValueException をスローします。
JSON 値を Java 値への変換
JSON 値を適切な Java プリミティブ型またはコレクションに変換するには、対応するメソッドを使用します。
| サブタイプ | メソッド | 生成される Java 型 |
|---|---|---|
| | |
| / / | / / |
| | |
| | |
| | |
特徴:
- 変換メソッドにより、多くの場合
チェックやダウンキャストが不要になります。instanceof - 数値の精度や範囲によっては例外をスローします(例:整数表現不可能な小数は失敗)。
には直接の変換メソッドはありません(JsonNull
を使用)。tryValue
JSON ドキュメントの進化への対処
オプションメンバーの処理
特定のメンバーが存在しない可能性を考慮する場合は、
tryGet() メソッドを使用します。
- 存在する場合:
を返す。Optional<JsonValue> - 不存在の場合: 空の
を返す(例外はスローしない)。Optional
null 値の処理
値が
JsonNull か否かを区別する場合は、tryValue() メソッドを使用します。
の場合: 空のJsonNull
を返す。Optional- それ以外の場合: その値を返す。
可変な構造の処理
異なる JDK バージョン間や時間経過で JSON 形式が変化する場合(例:スレッド ID が文字列から数値へ変更)に対応します。型スイッチ文を使用することで柔軟に処理できます。
long tid = switch (thread.get("tid")) { case JsonNumber jn -> jn.asLong(); case JsonString js -> Long.parseLong(js.asString()); default -> throw new JsonValueException("Unexpected type for \"tid\""); };
JSON ドキュメントの生成
JsonValue の toString() メソッドを呼び出すことで、コンパクトな JSON 文字列を取得できます。
: コンパクト形式(改行なし)。toString()
: 漂亮(pretty-printed)形式(改行付き・インデントあり)。Json.toDisplayString(json, indent)- 生成された出力は再び解析可能です。
JSON 数値
JSON 数値の精度と範囲に関する扱い:
: IEEE 754 64 ビット浮動小数点に変換します(無限大や NaN は返しません)。asDouble()
/asInt()
: 正確に表現できる場合にのみ変換されます。asLong()- 範囲外または非整数の場合、
をスローします。JsonValueException
- 範囲外または非整数の場合、
- 高精度データの保存: 精度を失わずに処理したい場合は
に直接変換します。java.math.BigDecimal
代替案について検討されなかった理由
- 完全な機能セットの提供:データバインドやストリーミングは API の大きさを増やし保守コストが高くなるため、排除されました。
- 外部 JSON ライブラリの統合:ライセンス・ガバナンス上の課題と相互運用性の緊張から除外されました。
- 「何もしない」方針:単純なタスクの実装を容易にし、外部依存性を排除するため、標準 API の導入が適切です。
- 重複メンバー名の許可:RFC 8259 に準拠し、曖昧さを避けるため厳格にエラーとしました。
- 構文拡張のサポート(例:JSON5):相互運用性とテストコストの観点からサポートしません。
テスト
- RFC 8259 の canonical forms に対する厳格なテストを実装しています。
- 外部の JSON 解析テストスイートを活用し、互換性を確保します。
リスクと仮定
- メモリ要件: JSON ドキュメント全体をメモリ上の文字列として保持するため、超大規模なドキュメントは処理できません(ミニマル設計のため)。
- 既存アプリケーションとの整合性: 既に外部ライブラリを使用しているアプリケーションとの競合可能性がありますが、長期的には標準化により利便性が向上します。
- 機能の拡張: インキュベーション期間中、データバインドやストリーミングが必要なユースケースが増えた場合は、それらへの対応を検討する予定です。
付録:天気予報の実装例
以下のコマンドで実行すると、カリフォルニア州サンタクララの 7 日間予報の平均気温が出力されます。
$ java --add-modules jdk.incubator.json Weather.java
出力:
WARNING: Using incubator modules: jdk.incubator.json 53.357142857142854
インキュベーター API の有効化
現在、JSON API はデフォルトで無効です。使用するには以下のようにモジュールを明示的に追加する必要があります。
コンパイルと実行
$ javac --add-modules jdk.incubator.json Weather.java $ java --add-modules jdk.incubator.json Weather
JShell での利用
$ jshell --add-modules jdk.incubator.json jshell> import jdk.incubator.json.* jshell> Json.parse("""{"name": "Today", "temperature": 54}""") $2 ==> {"name":"Today","temperature":54} jshell> $2.get("temperature").asInt() $3 ==> 54