CodeGym /コース /C# SELF /JSON と JSON Schema のバリデーション

JSON と JSON Schema のバリデーション

C# SELF
レベル 47, レッスン 4
使用可能

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 の主な要素

主要なキーが何を意味するか、例で見ていこう。

キー 説明 例
$schema
JSON Schema 標準のバージョンへのリンク
"https://json-schema.org/draft/2020-12/schema"
type
値の型(object, array, string, number など)
"type": "object"
properties
オブジェクトのプロパティの説明
"properties": { ... }
required
必須フィールドの配列
"required": ["id"]
items
配列要素の型の説明
"items": { ... }
enum
許可される値の一覧
"enum": ["A", "B"]
minimum
,
maximum
数値に対する制約
"minimum": 0
minLength
,
maxLength
文字列長の制約
"minLength": 3
pattern
文字列用の正規表現
"pattern": "^[a-zA-Z]+$"

視覚例:ユーザー配列のスキーマ

{
  "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 は進化しているので、使っているライブラリが最新版の仕様を全部サポートしてないことがある。検証が欠けている、あるいは挙動が微妙に違う場合があるよ。

2
タスク
C# SELF, レベル 47, レッスン 4
ロック未解除
JSON Schemaによるオブジェクト配列
JSON Schemaによるオブジェクト配列
1
アンケート/クイズ
JSONデータの扱い、レベル 47、レッスン 4
使用不可
JSONデータの扱い
JSONの構文と構造の基本
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION