1. なぜ JSON の検証が必要か
想像してみましょう。あなたはクラス User を作り、入力として常に次のような JSON が来ると想定しています。
{
"id": 42,
"name": "Alice",
"email": "alice@example.com"
}
ところが、こんなものが届くかもしれません。
{
"id": "四十二",
"name": 123,
"email": null,
"admin": true
}
あるいは、そもそも次のようなもの:
{
"username": "Alice"
}
良くて Jackson や Gson がデシリアライズ時に例外を投げます。悪い場合はフィールドに既定値を黙って入れてしまい、ビジネスコードが不正に動作します。これがサービスの設定ファイルなら、チーム総出で追いかける羽目になる“愉快な”バグに繋がりかねません。
JSON の検証とは、JSON 内の構造・型・値が所定のルール(スキーマ)に適合しているかを確認するプロセスです。データのパスポートコントロールのようなものです。通過できなければ——搭乗は不可、というわけです。
2. JSON Schema: 何で、どういう見た目か
JSON の世界には、データ構造を記述する公式標準 — JSON Schema があります。プログラムの要件に JSON が合っているかをチェックできる「チェックリスト」のようなものです。
JSON Schema 自体も JSON で、特別なキー(type、properties、required など)を使います。
最も簡単なスキーマの例
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string" },
"email": { "type": "string", "format": "email" }
},
"required": ["id", "name"]
}
ここで行っていること:
- オブジェクトを期待しています(type: "object")。
- オブジェクトには "id"、"name"、"email" の各フィールドがあり得ます(properties で定義)。
- "id" は必須の整数です(type: "integer")。
- "name" は必須の文字列です(type: "string")。
- "email" はメールアドレス形式の文字列です(キー format に "email")。
- required は必須フィールドの一覧を示します: "id" と "name"。
JSON に "id" や "name" がない、または型が違う場合、検証は失敗します。
JSON Schema でできること(概要)
- 型の指定(type: "string"、"integer"、"array"、"object"、"boolean"、"null")。
- 入れ子のオブジェクトや配列の記述(properties、items)。
- 必須/任意フィールド(required)。
- 文字列長や数値範囲の検証(minLength、maximum など)。
- フォーマット検証(format: "email"、"date"、"uri" など)。
- 列挙(enum: 許容値のリスト)。
- 文字列に対する正規表現(pattern)。
- 複合条件: anyOf、oneOf、allOf(高度なケース向け)。
3. Java における JSON 検証: ライブラリ概観
標準の Java ライブラリにはスキーマによる JSON 検証は含まれていません。しかし、人気のあるサードパーティ製ライブラリがあります。代表的なものは次の通りです。
- everit-org/json-schema — シンプル、無料、そして広く使われています。
- networknt/json-schema-validator — 高速で最新の仕様に対応。
- Jackson-module-jsonSchema — Jackson の拡張(ただし本格的な検証はサポートしていません)。
- Justify、Java JSON Tools — そのほかもありますが出現頻度は低めです。
この講義では everit-org/json-schema を扱います。初心者にも簡単で、ドキュメントが充実しており、余計な儀式も必要ありません。
everit-org/json-schema の導入
pom.xml(Maven)に依存関係を追加します。
<dependency>
<groupId>org.everit.json</groupId>
<artifactId>org.everit.json.schema</artifactId>
<version>1.14.2</version>
</dependency>
あるいは Gradle:
implementation 'org.everit.json:org.everit.json.schema:1.14.2'
4. 例: スキーマに基づく JSON 検証(手順)
実際に JSON を検証してみましょう。必要なのはスキーマ、JSON、そして少しのコードです。
スキーマの例(user-schema.json):
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string", "minLength": 2, "maxLength": 30 },
"email": { "type": "string", "format": "email" }
},
"required": ["id", "name"]
}
有効な JSON の例(user.json):
{
"id": 1,
"name": "Alice",
"email": "alice@example.com"
}
無効な JSON の例:
{
"id": "一",
"name": "",
"email": "not-an-email"
}
検証用コード
import org.everit.json.schema.Schema;
import org.everit.json.schema.loader.SchemaLoader;
import org.json.JSONObject;
import org.json.JSONException;
import org.json.JSONTokener;
import org.everit.json.schema.ValidationException;
import java.nio.file.Files;
import java.nio.file.Paths;
public class JsonValidationExample {
public static void main(String[] args) throws Exception {
// スキーマをファイルから読み込む
String schemaString = new String(Files.readAllBytes(Paths.get("user-schema.json")));
JSONObject rawSchema = new JSONObject(new JSONTokener(schemaString));
Schema schema = SchemaLoader.load(rawSchema);
// 検証用の JSON を読み込む
String jsonString = new String(Files.readAllBytes(Paths.get("user.json")));
JSONObject json = new JSONObject(new JSONTokener(jsonString));
// 検証
try {
schema.validate(json); // すべて問題なければ何も起こりません
System.out.println("JSON は有効です!");
} catch (ValidationException e) {
System.out.println("JSON は無効です!");
for (String msg : e.getAllMessages()) {
System.out.println("エラー: " + msg);
}
}
}
}
コードに関する補足:
- Schema クラスとローダー SchemaLoader.load(...) を使用しています。
- メソッド schema.validate(json) はスキーマに合致しない場合に例外を投げます。
- catch ブロックで getAllMessages() によりすべてのエラーを取得できます。
アプリへの統合方法は?
通常、スキーマはリソース(たとえば resources ディレクトリ)に置きます。Java オブジェクトにデシリアライズする前に JSON を検証します。問題なければデシリアライズして処理を続けます。
5. 検証エラーの扱い
JSON が検証に通らない場合、ライブラリは ValidationException を投げます。メッセージには、どこがスキーマに合致していないかの一覧が含まれます。
エラー出力の例
上の無効な JSON に対する出力は概ね次のようになります。
JSON は無効です!
エラー: #: required key [id] not found
エラー: #/name: expected minLength: 2, actual: 0
エラー: #/email: String [not-an-email] is invalid against requested format [email]
エラー: #/id: expected type: Integer, found: String
エラーの読み方:
- required key [id] not found — 必須フィールドが欠けています。
- expected minLength: 2, actual: 0 — 文字列が短すぎます。
- String [...] is invalid against requested format [email] — メールアドレスの形式が不正です。
- expected type: Integer, found: String — 型が一致しません。
重要: メッセージは英語の場合がありますが、内容は十分分かりやすいです。
エラーをユーザーにどう見せるか?
エラーを一覧にまとめて、たとえば REST API や GUI で返すことができます。これにより、入力データのどこが問題かを素早く把握できます。
6. 実践: オブジェクト配列の検証
1 つのオブジェクトではなく配列を検証することもよくあります。
[
{ "id": 1, "name": "Alice", "email": "alice@example.com" },
{ "id": 2, "name": "Bob" },
{ "id": "これは何?", "name": 123, "email": "not-an-email" }
]
スキーマ:
{
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string", "minLength": 2, "maxLength": 30 },
"email": { "type": "string", "format": "email" }
},
"required": ["id", "name"]
}
}
検証のやり方は同じで、JSON には配列を渡すだけです。エラーには、問題がある要素のインデックスが含まれます(例: [#/2/id])。
7. JSON 検証でありがちなミス
誤り №1: データ型の不一致。 数値のはずが文字列("id": "123")で来ることがよくあります。スキーマは integer を期待しているため検証は通りません。データ源を制御できない場合は、スキーマを修正するか、事前にデータを変換してください。
誤り №2: 必須フィールドの欠落。 スキーマで必須("required": ["id","name"])なのに JSON に存在しない場合、エラーになります。フロントエンドが送信し忘れたり、API が変更されたりと、意外と起きがちです。
誤り №3: 余計なフィールド。 デフォルトでは JSON Schema は余分なフィールドを許容します。厳密にしたい場合は "additionalProperties": false を忘れずに。これがないと JSON にいくらでも「関係ない」フィールドが入ってしまいます。
誤り №4: スキーマのバージョン/文法の誤り。 使用しているスキーマのバージョンに存在しないキーを使ったり、タイプミスがあると、バリデータはスキーマを読み込めません。https://www.jsonschemavalidator.net/ などで検証しましょう。
誤り №5: エラー処理が不十分。 最初の例外だけを捕捉してユーザーに詳細を見せないと、JSON の何が悪いのか分かりにくくなります。getAllMessages() を使ってすべてのエラーを出力しましょう。
誤り №6: フォーマット検証に頼りすぎる。 "format": "email" や "date" のチェックは比較的「浅い」ことがあります。厳密な検証が必要なら、コード側で追加チェックを行ってください。
GO TO FULL VERSION