1. はじめに
オブジェクトを JSON に変換したり(逆も)できるようになったとする。でも、プログラムの入力にちょっと変なデータが入ってきたらどうする? 例えば、期待していた JSON はこんな感じだった:
{
"id": 123,
"name": "Alice",
"email": "alice@example.com"
}
でも実際に来たのは:
{
"name": 42,
"id": "not a number"
}
C# のシリアライザは無理やりデシリアライズを試みるけど、多くの場合ランタイムエラーになったり、データが失われたり、最悪ビジネスがクラッシュする。データを次に回す前やDBに保存する前、ネット越しに渡す前に、データが有効かチェックしておくべきだよ — そうしないとコードは命綱のないアクロバットみたいになる。
バリデーションが必要な理由:それは自動で「この JSON がルールに合っているか」を確認する仕組み。型、必須フィールド、範囲、構造などをチェックするんだ。
2. JSON のバリデーション方法
C# / .NET では主に三つのアプローチがあるよ:
- 自前のコードでのバリデーション:オブジェクトを手でパースしてチェックを書き、エラーなら例外を投げる。
- モデルにバリデーション属性を付ける:例えば [Required], [Range], [EmailAddress] (System.ComponentModel.DataAnnotations のやつ)。
- JSON Schema を使ったバリデーション — 今日の主役だよ!
JSON Schema は、ドキュメントがどうあるべきかを形式的に記述する標準だ。スキーマ自体も JSON で、どのフィールドが必要か、どの型か、どんな値が許されるかを定義できる。
スキーマで表現できることの例:
- どのフィールドが存在すべきか。
- 期待される型(string、array、number、object...)。
- 必須フィールド/任意フィールド。
- 値の範囲(例:年齢は 0 から 150 など)。
- パターン、長さ、列挙値など。
最もシンプルなスキーマの例
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string" }
},
"required": ["id", "name"]
}
3. JSON Schema の主な要素
主要なキーが何を意味するか、例で見ていこう。
| キー | 説明 | 例 |
|---|---|---|
|
JSON Schema 標準のバージョンへのリンク | |
|
値の型(object, array, string, number など) | |
|
オブジェクトのプロパティの説明 | |
|
必須フィールドの配列 | |
|
配列要素の型の説明 | |
|
許可される値の一覧 | |
, |
数値に対する制約 | |
, |
文字列長の制約 | |
|
文字列用の正規表現 | |
視覚例:ユーザー配列のスキーマ
{
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string", "minLength": 2, "maxLength": 50 },
"email": { "type": "string", "format": "email" }
},
"required": ["id", "name"]
}
}
4. C# で JSON をスキーマに対して検証する方法
現状(.NET 9 時点)では .NET に組み込みの JSON Schema サポートはない。実務では外部ライブラリを使うことが多い: NJsonSchema や Newtonsoft.Json.Schema(Json.NET Schema)など。
Newtonsoft.Json.Schema (Json.NET Schema) のインストール
ターミナルで実行(プロジェクトフォルダ内):
dotnet add package Newtonsoft.Json.Schema
重要: Newtonsoft.Json.Schema は商用ライブラリです(非商用目的なら無料で使えます)。個人の pet プロジェクトにはぴったりです。
JSON がスキーマに合うかチェックする例
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Schema;
string schemaJson = @"{
'type': 'object',
'properties': {
'id': { 'type': 'integer' },
'name': { 'type': 'string' },
'email': { 'type': 'string', 'format': 'email' }
},
'required': ['id', 'name']
}";
// 例えば、こんなJSONがあるとする
string json = @"{
'id': 123,
'name': 'Alice',
'email': 'alice@example.com'
}";
// まずスキーマをパースする
JSchema schema = JSchema.Parse(schemaJson);
// JSON自体をJTokenにパースする
JToken jsonObj = JToken.Parse(json);
// JSONがスキーマに合致するか検証する
bool valid = jsonObj.IsValid(schema, out IList<string> errors);
if (valid)
{
Console.WriteLine("JSONは有効だよ!");
}
else
{
Console.WriteLine("JSONは無効だよ!");
foreach (var error in errors)
Console.WriteLine(error);
}
アプリケーションで検証をどう使うか?
普通は:まずバリデーション(IsValid)をして、問題なければデシリアライズ(JsonConvert.DeserializeObject<T>)して、その後にビジネスロジックを実行する。これで不正なデータを早い段階で弾ける。
if (jsonObj.IsValid(schema))
{
// 大丈夫、デシリアライズしてOK
var person = JsonConvert.DeserializeObject<Person>(json);
}
else
{
// 処理を中止!データが不正だよ。
}
5. 実務での JSON Schema の使い方
いつバリデーションが必要か?
- APIでデータを受け取るとき(特に外部システムや多様なクライアントから来る場合)。
- マイクロサービス間やデータベース間でデータを移行するとき。
- スキーマを元に UI フォームを自動生成したり自動入力する場合。
- 面接で「変な JSON が来たらどうする?」ってよく問われるので準備しておくと良い。
興味深いポイント
- フォーマット検証:"format" に "email" や "date-time" がある。
- 複合ルール:anyOf, oneOf, allOf。
- ネストしたオブジェクトや配列の検証。
- プロジェクトに合わせてスキーマを拡張することも可能。
6. 実践的な大きめの例
入力データ(ユーザーのリスト):
[
{ "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": 50 },
"email": { "type": "string", "format": "email" }
},
"required": ["id", "name"]
}
}
.NET でのチェック:
string jsonArray = @"[ ... ]"; // 上記参照
string schemaJson = @"{ ... }"; // 上記参照
JSchema schema = JSchema.Parse(schemaJson);
JToken arrayToken = JToken.Parse(jsonArray);
bool isValid = arrayToken.IsValid(schema, out IList<string> errs);
if (!isValid)
{
foreach (var error in errs)
Console.WriteLine(error);
// エラーメッセージ例:
// "String 'これは何?' is not a valid integer."
// "Integer 123 is not a valid string."
// "String 'not-an-email' is not a valid email address."
}
7. JSON スキーマでよくあるミス
ミス №1: スキーマが実際のデータと合っていない。 モデルを変えたのにスキーマを更新し忘れる。結果、バリデーションがエラーを見逃したり、逆に正しいデータをブロックしたりする。
ミス №2: スキーマの型とモデルの型が不一致。 例えばモデルの id が文字列に変わったのにスキーマは integer のまま、みたいなケース。バリデータが怒る。
ミス №3: ビジネス上必須のフィールドを見落とす。 スキーマで必須にしていなくてもビジネスロジックがそのフィールドを前提にしていると、無いと落ちる。
ミス №4: データのフォーマットを過信する。 string 型のフィールドでも email や日付のフォーマットが正しくない場合がある。format や追加チェックを使おう。
ミス №5: ライブラリの実装が標準に追いついていない。 JSON Schema は進化しているので、使っているライブラリが最新版の仕様を全部サポートしてないことがある。検証が欠けている、あるいは挙動が微妙に違う場合があるよ。
GO TO FULL VERSION