JSON Schema: What It Is and How to Use It

Learn JSON Schema from scratch. Understand types, constraints, composition keywords, $ref, and see a complete practical example for API validation.

JSONTech チームFebruary 1, 202511 min read

JSONスキーマとは?

JSONスキーマは、JSONデータの構造を記述するための語彙です。これを契約として考えてください:JSONドキュメントが持つべきフィールド、フィールドの型、満たすべき制約を定義します。ドキュメントがスキーマに準拠している場合、それは有効です。そうでない場合、バリデーターは何が間違っているかを正確に教えてくれます。

これは重要です。なぜなら、JSON自体は構造的に許容的だからです。JSON仕様には、APIが数値を期待するところで文字列を返したり、必須フィールドを省略したりすることを防ぐものはありません。JSONスキーマはそのギャップを埋めます。

JSONスキーマが重要な理由

  • API契約。 APIが期待するものと返すものを正確に定義します。クライアントとサーバーチームは、ペイロードを独立して検証できます。
  • データ検証。 フォーム送信、Webhookペイロード、設定ファイル、またはデータベース書き込みの境界で不正なデータをキャッチします。
  • ドキュメント。 スキーマは機械可読のドキュメントです。ツールはそれから人間可読のドキュメント、フォーム、さらにはモックデータを生成できます。
  • コード生成。 スキーマから直接TypeScriptインターフェース、Go構造体、またはPythonデータクラスを生成します。

基本:タイプ、プロパティ、必須

すべてのJSONスキーマはtypeから始まります。これは、バリデーターにトップレベルで期待される値の種類を伝えます:

{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "ユーザーのフルネーム"
    },
    "age": {
      "type": "integer",
      "description": "年齢"
    }
  },
  "required": ["name"]
}

このスキーマは、「nameプロパティ(文字列、必須)を持つJSONオブジェクトを期待し、オプションのageプロパティ(整数)を持つ」と言っています。nameが欠けているか、非文字列のnameを提供しているオブジェクトは無効です。

すべてのJSONスキーマタイプ

JSONスキーマは7つの基本タイプをサポートしています。以下はそれぞれの最小スキーマ例です:

タイプ有効な値スキーマ例
string"hello", ""{"type": "string"}
number3.14, -1, 0{"type": "number"}
integer42, -7{"type": "integer"}
booleantrue, false{"type": "boolean"}
nullnull{"type": "null"}
object{"key": "value"}{"type": "object", "properties": {...}}
array[1, 2, 3]{"type": "array", "items": {...}}

オブジェクトの詳細

propertiesを使用して期待されるフィールドを定義し、requiredで必須のものをリストし、additionalPropertiesで追加フィールドが許可されるかどうかを制御します:

{
  "type": "object",
  "properties": {
    "id": { "type": "integer" },
    "email": { "type": "string", "format": "email" }
  },
  "required": ["id", "email"],
  "additionalProperties": false
}

additionalProperties: falseを設定すると、propertiesにリストされていないキーを含むオブジェクトは拒否されます。これは厳密なAPI契約に役立ちます。

配列の詳細

itemsを使用して各要素がどのように見えるべきかを定義し、minItems / maxItemsで長さを制約します:

{
  "type": "array",
  "items": { "type": "string" },
  "minItems": 1,
  "maxItems": 10,
  "uniqueItems": true
}

これは1から10のユニークな文字列の配列を受け入れます。空の配列や重複のある配列は検証に失敗します。

制約と検証キーワード

基本タイプを超えて、JSONスキーマは各タイプに対して細かい制約を提供します:

文字列制約

キーワード説明
minLength最小文字数"minLength": 1
maxLength最大文字数"maxLength": 255
pattern文字列が一致する必要がある正規表現"pattern": "^[A-Z]{2}\\\\d{4}$"
formatセマンティックフォーマットのヒント"format": "email"
enum許可される値"enum": ["active", "inactive"]

数値制約

キーワード説明
minimum値はこれ以上でなければならない"minimum": 0
maximum値はこれ以下でなければならない"maximum": 100
exclusiveMinimum値はこれより大きくなければならない"exclusiveMinimum": 0
exclusiveMaximum値はこれより小さくなければならない"exclusiveMaximum": 1000
multipleOf値はこれで割り切れる必要がある"multipleOf": 0.01

スキーマの組み合わせ:allOf、anyOf、oneOf、not

構成キーワードを使用すると、単純なスキーマから複雑なスキーマを構築できます:

  • allOf — データはすべてのリストされたスキーマに対して有効でなければなりません。複数の制約を組み合わせたり、基本スキーマを拡張したりするために使用されます。
  • anyOf — データは少なくとも1つのリストされたスキーマに対して有効でなければなりません。複数のフォーマットを受け入れるフィールドに便利です。
  • oneOf — データは正確に1つのリストされたスキーマに対して有効でなければなりません。識別されたユニオンに適しています。
  • not — データは指定されたスキーマに対して無効でなければなりません。
{
  "oneOf": [
    {
      "type": "object",
      "properties": {
        "type": { "const": "email" },
        "address": { "type": "string", "format": "email" }
      },
      "required": ["type", "address"]
    },
    {
      "type": "object",
      "properties": {
        "type": { "const": "phone" },
        "number": { "type": "string", "pattern": "^\\\\+?[0-9]{7,15}$" }
      },
      "required": ["type", "number"]
    }
  ]
}

このスキーマは、メール連絡先または電話連絡先のいずれかを受け入れますが、両方は受け入れません — タグ付きユニオンをモデル化するクリーンな方法です。

$refを使用した再利用性

スキーマはすぐに繰り返しになります。$refキーワードを使用すると、再利用可能な定義を参照できます:

{
  "type": "object",
  "properties": {
    "billing_address": { "$ref": "#/$defs/address" },
    "shipping_address": { "$ref": "#/$defs/address" }
  },
  "$defs": {
    "address": {
      "type": "object",
      "properties": {
        "street": { "type": "string" },
        "city": { "type": "string" },
        "zip": { "type": "string", "pattern": "^[0-9]{5}(-[0-9]{4})?$" }
      },
      "required": ["street", "city", "zip"]
    }
  }
}

billing_addressshipping_addressは、重複なしで同じ構造を共有します。$defsセクション(古いドラフトではdefinitionsと呼ばれる)は、再利用可能なスキーマを保存するための慣例的な場所です。

完全な例:ユーザー登録の検証

以下は、ユーザー登録ペイロードを検証するための実際のスキーマです。これまでにカバーしたほとんどの機能を使用しています:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "UserRegistration",
  "description": "POST /api/registerエンドポイントのスキーマ",
  "type": "object",
  "properties": {
    "username": {
      "type": "string",
      "minLength": 3,
      "maxLength": 30,
      "pattern": "^[a-zA-Z0-9_]+$",
      "description": "英数字のユーザー名、3-30文字"
    },
    "email": {
      "type": "string",
      "format": "email",
      "maxLength": 254
    },
    "password": {
      "type": "string",
      "minLength": 8,
      "maxLength": 128,
      "description": "最低8文字"
    },
    "age": {
      "type": "integer",
      "minimum": 13,
      "maximum": 150
    },
    "role": {
      "type": "string",
      "enum": ["user", "moderator"],
      "default": "user"
    },
    "acceptedTerms": {
      "type": "boolean",
      "const": true
    },
    "tags": {
      "type": "array",
      "items": { "type": "string", "maxLength": 20 },
      "maxItems": 5,
      "uniqueItems": true
    }
  },
  "required": ["username", "email", "password", "acceptedTerms"],
  "additionalProperties": false
}

各フィールドに明確で強制可能な制約があることに注意してください。バリデーターは、2文字のユーザー名、13歳未満の年齢、重複タグ、またはスキーマにリストされていない余分なフィールドを拒否します。

自分で試してみてください: 任意のJSONを貼り付けて、JSONスキーマジェネレーターでスキーマを自動生成します。

実世界におけるJSONスキーマ

OpenAPI / Swagger

OpenAPIは、リクエストボディ、レスポンス形状、クエリパラメータを定義するためにJSONスキーマ(いくつかの拡張を含む)を使用します。OpenAPI仕様のすべてのschemaブロックはJSONスキーマです。OpenAPIドキュメントを書く場合、すでにJSONスキーマを書いています。

フォーム検証

react-jsonschema-formやJavaScript用の最速のJSONスキーマバリデーターであるajvのようなライブラリは、スキーマを使用してランタイムでフォームを生成および検証します。スキーマを一度定義すれば、バックエンドとフロントエンドの両方が同じルールに対して検証できます。

データベーススキーマ

MongoDBは、コレクションレベルでのJSONスキーマ検証をサポートしています。スキーマに準拠しないドキュメントを挿入または更新時に拒否する$jsonSchemaバリデーターを設定できます。

設定ファイル

VS Code、ESLint、その他多くのツールは、設定ファイルを検証するためにJSONスキーマを使用します。tsconfig.jsonを編集するときに得られる自動補完は、JSONスキーマによって提供されています。

ドラフトバージョンの比較

JSONスキーマは、いくつかのドラフトを通じて進化してきました。以下は、遭遇することになるドラフトです:

ドラフト主要な追加機能ステータス
Draft-042013コア語彙、$ref、基本タイプレガシー(依然として広く使用されている)
Draft-062017constcontainspropertyNamesレガシー
Draft-072018if/then/elsereadOnlywriteOnly広くサポートされている
2019-092019$defsunevaluatedPropertiesdependentRequired主要なバリデーターによってサポートされている
2020-122020prefixItems(タプル検証の置き換え)、動的$ref現行 / 推奨

新しいプロジェクトには2020-12を使用してください。既存のツールとの最大の互換性が必要な場合は、Draft-07が安全な選択です — ほぼ普遍的なライブラリサポートがあります。

避けるべき一般的な間違い

  • requiredを忘れる。 propertiesの下に定義されたプロパティはデフォルトでオプションです。フィールドが存在する必要がある場合は、requiredにリストしてください。
  • numberintegerを混同する。 numberは小数を受け入れますが、integerは受け入れません。ID、カウント、その他の整数値にはintegerを使用してください。
  • 早すぎる制約の過剰。 実際のエラーをキャッチする最小スキーマから始めてください。後で制約を厳しくすることはできますが、緩めることは破壊的な変更です。
  • $refを使用しない。 同じサブスキーマを複数の場所に複製することは、メンテナンスの悪夢です。共有構造を$defsに抽出してください。
  • format検証を無視する。 デフォルトでは、ほとんどのバリデーターはformatを注釈として扱い、制約とは見なさない。フォーマット検証を明示的に有効にする必要があります(例:ajv.addFormat()または{ validateFormats: true }を渡す)。

自分で試してみてください: 任意のJSONサンプルからスキーマを生成し、JSONスキーマジェネレーターで制約をカスタマイズして要件に合わせてください。

関連ツール