← 記事一覧

JSON Schema とは? 例で学ぶ実用ガイド

JSON Schema は JSON データの構造と制約を記述する語彙。中心的なキーワード、現実例、JavaScript・Python・ブラウザでの検証方法を学ぶ。

JSON Schema は、JSON ドキュメントの構造、制約、型を記述するための語彙です。有効な JSON がどうあるべきか — どのキーが必須か、各値はどの型でなければならないか、どの値が許されるか — を正確に定義し、その規則に対して任意の JSON ドキュメントを自動的に検証できます。本ガイドでは、JSON Schema とは何か、どう書くか、そして JavaScript、Python、ブラウザでそれを使って JSON を検証する方法を説明します。

JSON Schema とは何か

JSON Schema 自体が JSON ドキュメントです。データベーススキーマがテーブルを記述するのと同じように、別の JSON ドキュメントを記述します — データの想定される形、型、制約を宣言します。JSON Schema はバリデータ — ライブラリ、API、フォームジェネレータ、IDE — に、ある JSON において「有効」とは何を意味するかを伝えます。

仕様は json-schema.org で維持され、現在は Draft 2020-12 です。OpenAPI(REST API 記述の標準)、JSON Forms(スキーマからの UI 生成)、IDE ツール(VS Code は package.jsontsconfig.json のオートコンプリートに JSON Schema を使う)、そして数えきれない社内データパイプラインで使われています。

最小限の JSON Schema 例

ユーザーを表す JSON ドキュメントと、その想定構造を記述するスキーマを示します:

// 検証対象の JSON ドキュメント
{
  "id": 42,
  "name": "Alice",
  "email": "alice@example.com",
  "age": 30,
  "active": true
}
// JSON Schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "required": ["id", "name", "email"],
  "properties": {
    "id":     { "type": "integer" },
    "name":   { "type": "string", "minLength": 1 },
    "email":  { "type": "string", "format": "email" },
    "age":    { "type": "integer", "minimum": 0, "maximum": 150 },
    "active": { "type": "boolean" }
  },
  "additionalProperties": false
}

このスキーマが述べているのは: ドキュメントはオブジェクトでなければならない; idnameemail は必須; age は存在する場合 0 から 150 の間; そして列挙された 5 つ以外のキーは許されない、ということです。

中心となるキーワード

type

値の JSON 型を指定します。有効な型は "string""number""integer""boolean""array""object""null"。配列で複数型を許可することもできます:

{ "type": ["string", "null"] }  // 値は文字列か null

properties

オブジェクトの各キーに対するスキーマを定義します。各エントリ自体がスキーマです:

{
  "type": "object",
  "properties": {
    "firstName": { "type": "string" },
    "lastName":  { "type": "string" },
    "age":       { "type": "integer", "minimum": 0 }
  }
}

required

オブジェクトに存在しなければならないキー名の配列。properties に挙げられていても required にないキーはオプション — 欠けていてもよいが、存在する場合はそのスキーマに合致しなければなりません。

items

配列内の各要素に対するスキーマを定義します。次のスキーマは文字列の配列を要求します:

{
  "type": "array",
  "items": { "type": "string" }
}

文字列の制約

{
  "type": "string",
  "minLength": 1,
  "maxLength": 100,
  "pattern": "^[a-zA-Z0-9_]+
quot; // 正規表現パターン }

数値の制約

{
  "type": "number",
  "minimum": 0,
  "maximum": 1,
  "multipleOf": 0.01   // 0.01 の倍数でなければならない
}

enum

値を許容される固定の集合に制限します:

{ "enum": ["pending", "active", "suspended", "deleted"] }

additionalProperties

properties にないキーを許可するかを制御します。false に設定すると想定外のキーを拒否します — 厳格な API 契約に有用です:

{ "additionalProperties": false }

現実的な JSON Schema 例

EC API における商品のスキーマです — OpenAPI 定義や社内データ契約で目にするタイプのものです:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://example.com/schemas/product.json",
  "title": "Product",
  "description": "A product available for purchase",
  "type": "object",
  "required": ["id", "name", "price", "category"],
  "properties": {
    "id": {
      "type": "integer",
      "description": "Unique product identifier"
    },
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    },
    "price": {
      "type": "number",
      "minimum": 0,
      "description": "Price in USD"
    },
    "category": {
      "type": "string",
      "enum": ["electronics", "clothing", "books", "home", "sports"]
    },
    "tags": {
      "type": "array",
      "items": { "type": "string" },
      "uniqueItems": true
    },
    "dimensions": {
      "type": "object",
      "properties": {
        "width":  { "type": "number", "minimum": 0 },
        "height": { "type": "number", "minimum": 0 },
        "depth":  { "type": "number", "minimum": 0 }
      },
      "required": ["width", "height", "depth"]
    },
    "inStock": { "type": "boolean" }
  },
  "additionalProperties": false
}

スキーマで JSON を検証する方法

jsonschema バリデータ を選びますか? 定番は Ajv(JavaScript)、jsonschema(Python)、そして jsonschemavalidator.net のようなオンラインサービスです。逆方向 — 例から json schema を生成する、または json schema from json ドキュメントから生成する — が必要なら、Python では GenSON、ブラウザでは transform.tools を試してください。これらは json schema generator ツールで、サンプルから出発点となるスキーマを推論します。あとから requiredadditionalProperties、値の制約を手で締めてください。

JavaScript (Ajv)

Ajv は JavaScript エコシステムで最も広く使われている JSON Schema バリデータです。Draft-07 と Draft 2020-12 をサポートします。

npm install ajv
import Ajv from 'ajv';

const ajv = new Ajv();

const schema = {
  type: 'object',
  required: ['id', 'name', 'email'],
  properties: {
    id:    { type: 'integer' },
    name:  { type: 'string', minLength: 1 },
    email: { type: 'string' },
  },
  additionalProperties: false,
};

const validate = ajv.compile(schema);

const data = { id: 42, name: 'Alice', email: 'alice@example.com' };
const valid = validate(data);

if (!valid) {
  console.error(validate.errors);
} else {
  console.log('Valid!');
}

ajv.compile(schema) は再利用可能な検証関数を返します。一度コンパイルしてその結果を何度も呼び出す方が、呼び出しごとに再コンパイルするより大幅に高速です。

JavaScript(ブラウザ、依存なし)

単純な型と必須フィールドのチェックのためだけに依存を追加したくないなら手で検証してもよいですが、自明でないチェックには Ajv を使ってください。スキーマ検証のロジックは十分に複雑なので、手書きはエラーを誘発します。

Python (jsonschema)

pip install jsonschema
import json
import jsonschema
from jsonschema import validate, ValidationError

schema = {
    "type": "object",
    "required": ["id", "name", "email"],
    "properties": {
        "id":    {"type": "integer"},
        "name":  {"type": "string", "minLength": 1},
        "email": {"type": "string"},
    },
    "additionalProperties": False,
}

data = {"id": 42, "name": "Alice", "email": "alice@example.com"}

try:
    validate(instance=data, schema=schema)
    print("Valid!")
except ValidationError as e:
    print(f"Invalid: {e.message}")

スキーマで JSON ファイルを検証する

import json
import jsonschema

with open('schema.json') as f:
    schema = json.load(f)

with open('data.json') as f:
    data = json.load(f)

jsonschema.validate(instance=data, schema=schema)

ブラウザ (fixjson)

コードを書かずに JSON ドキュメントが構造的に妥当か確認したいなら — fixjson の JSON バリデータ が即座に構文を確認しエラーを示します。JSON を貼り付ければ、構文の問題が各エラーの正確な行と位置とともにインラインで表示されます。

完全なスキーマ検証(型、必須フィールド、制約のチェック)には、上記のライブラリベースの方法か、jsonschemavalidator.net のような専用スキーマテストツールを使ってください。

$ref: スキーマの再利用

実務のスキーマはすべてをインラインで定義したりしません。$ref キーワードを使えば、別のスキーマを $id か同じドキュメント内の JSON Pointer パスで参照でき、スキーマを DRY かつ組み合わせ可能に保てます。

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$defs": {
    "address": {
      "type": "object",
      "required": ["street", "city", "country"],
      "properties": {
        "street":  { "type": "string" },
        "city":    { "type": "string" },
        "country": { "type": "string", "minLength": 2, "maxLength": 2 }
      }
    }
  },
  "type": "object",
  "properties": {
    "billingAddress":  { "$ref": "#/$defs/address" },
    "shippingAddress": { "$ref": "#/$defs/address" }
  }
}

$defs は単一ファイル内で再利用可能なサブスキーマを定義する標準の場所です(Draft 2019-09 で definitions を置き換えました)。

スキーマを組み合わせる: allOf、anyOf、oneOf

JSON Schema には「全てに合致しなければならない」「少なくとも一つに合致」「ちょうど一つに合致」を表現する 3 つの合成キーワードがあります:

// allOf: 列挙されたすべてのスキーマを満たす必要がある
{
  "allOf": [
    { "type": "object" },
    { "required": ["id"] },
    { "properties": { "id": { "type": "integer" } } }
  ]
}

// anyOf: 少なくとも 1 つのスキーマを満たす必要がある
{
  "anyOf": [
    { "type": "string" },
    { "type": "null" }
  ]
}

// oneOf: ちょうど 1 つのスキーマを満たす必要がある(互いに排他であること)
{
  "oneOf": [
    { "properties": { "type": { "const": "circle" } }, "required": ["radius"] },
    { "properties": { "type": { "const": "rectangle" } }, "required": ["width", "height"] }
  ]
}

format キーワード: date-time、email、uri、uuid

生の型を越えて、JSON Schema の format キーワードは文字列に想定される — ISO 日時、メールアドレス、URI、UUID、IP アドレスなど — を注釈付けします。形は次のとおり:

{
  "type": "object",
  "properties": {
    "id":         { "type": "string", "format": "uuid" },
    "created_at": { "type": "string", "format": "date-time" },
    "email":      { "type": "string", "format": "email" },
    "homepage":   { "type": "string", "format": "uri" }
  }
}

微妙ですが重要な点: Draft 2020-12(および 2019-09) では format キーワードは既定で注釈 であり、バリデータが設定されない限りフォーマットを強制しません。Draft-07 以前では、一部のバリデータでは既定でアサーションでした。Ajv で厳格にするには:

import Ajv from 'ajv';
import addFormats from 'ajv-formats';

const ajv = new Ajv();
addFormats(ajv);              // date-time、email、uri、uuid などを登録

// 2020-12 ではメタスキーマで format アサーションを有効化するか、Ajv の
// バージョンに応じて { validateFormats: true } を明示的に渡す必要があります。

一般的な format 値: date-timedatetimedurationemailidn-emailhostnameipv4ipv6uriuri-referenceuuidregexjson-pointer

format は手がかりとして扱い、セキュリティ境界とはみなさないこと — format: "email" を通った文字列でも依然として信頼できない入力です。配送可能性や真の有効性のためには帯域外で検証してください(DNS、確認メールの送信、自分で管理する正規表現の実行)。

JSON Schema の典型的な間違い

「properties」と「required」を混同する

properties にキーを並べただけでは必須にはなりません。properties のキーは「そのキーが現れるならば」適用されるスキーマを定義します。存在を強制するには、それを required にも入れる必要があります。

「number」のつもりで「integer」を使う

"type": "integer"3.14 を拒否します。フィールドが任意の数(小数を含む)であってよいなら、"type": "number" を使ってください。

「additionalProperties」がデフォルトで true であることを忘れる

既定で JSON Schema は properties に列挙されていない追加プロパティを許可します。これは意図的 — スキーマは Postel の法則に従い、既定では開かれているべきだという考え方です。クローズドなオブジェクトが欲しいなら明示的に "additionalProperties": false を設定してください。

$schema を指定しない

draft が違えば意味も違います。$schema キーワードを省くとバリデータがどのバージョンを使うか推測することになります。常に含めましょう:

{ "$schema": "https://json-schema.org/draft/2020-12/schema" }

JSON Schema 検証をセキュリティ検証と捉える

JSON Schema は構造と型を検証する — サニタイゼーションツールではありません。"type": "string" を通る文字列でも、SQL インジェクションや XSS ペイロードなど悪意あるコンテンツを含む可能性があります。スキーマ検証は最初のゲートであり、完全なセキュリティ対策ではありません。

JSON Schema と OpenAPI

OpenAPI(旧 Swagger)はリクエストボディ、レスポンスボディ、パラメータの記述に JSON Schema のサブセットを使います。OpenAPI 仕様を書いたことがあるなら、JSON Schema を書いたことがあります — もっとも OpenAPI は拡張と制限を含む修正された方言を用います。

つまり JSON Schema の知識はそのまま API 設計に転用できます。OpenAPI ドキュメントの components/schemas セクションは JSON Schema の集合であり、Stoplight、Redoc、Swagger UI などのツールはそれを自動的に対話的ドキュメントに描画します。

Ajv の実践

実プロジェクトでは元が取れるフラグが二つあります:

  • strict: true(現代の Ajv では既定) — スキーマ コンパイル時に未知のキーワードや未知の format を拒否し、データが黙って通過する前に "minimun" のような誤字や欠落した $schema を捕えます。
  • allErrors: true — 最初で止まらず、すべての検証失敗を集めます。ユーザー向けメッセージには ajv.errorsText() と相性がよいです。

スキーマからテスト用やフィクスチャ用の JSON を生成するには json-schema-faker を使います: スキーマを辿り、合致する値を生成します(型、範囲、列挙、そして ajv-formats と組み合わせれば format まで)。プロパティベーステストやスキーマからの UI 初期値生成に便利です。

既存の JSON から JSON Schema を生成する

JSON データから出発点のスキーマを生成したい場合、いくつかのツールが自動化します:

  • quicktype.io — サンプル JSON からスキーマ(と TypeScript、Go、Python 等の型定義)を推論
  • generate-schema (npm) —— generate-schema json schema.json data.json
  • Python: gensonpip install genson、その後 SchemaBuilder が 1 つ以上のサンプルオブジェクトからスキーマを推論

生成されたスキーマは出発点であり、最終成果物ではありません。サンプルデータから型を推論しますが、ビジネスルールはわかりません — 文字列フィールドがメールアドレスだとか、整数が正でなければとか、二つのフィールドが排他であるといったことを推論できません。本番投入前には常に見直して締めてください。

よくある質問

JSON Schema は何のためにある?

JSON データの構造を記述し検証するためです — どのキーが必須か、各値はどんな型でなければならないか、どの値が許されるか。OpenAPI、package.json/tsconfig.json の IDE オートコンプリート、フォーム生成、データパイプライン契約などを支えます。

JSON と JSON Schema の違いは?

JSON はデータ、JSON Schema は有効なデータがどうあるべきかを記述する別の JSON ドキュメントです。フォーマット自体に馴染みがないなら JSON とは何か? から始めてください。

どの JSON Schema ドラフトを使えばよい?

Draft 2020-12 が現行で、新しいスキーマには推奨です。Draft-07 はツール対応が広いためまだよく見られます。常に $schema でバージョンを宣言してバリデータに推測させないこと。

スキーマに対して JSON をどう検証する?

JavaScript では Ajv、Python では jsonschema ライブラリ(上の例)、もしくはウェブのバリデータを使います。まず JSON が構文的に有効か確認するには JSON の検証方法 を見るか、バリデータ に通してください。

JSON Schema は format(email、date-time、uri…)を検証する?

Draft 2020-12 / 2019-09 では format キーワードは既定で注釈です — バリデータは設定されたときのみ強制します。Ajv では ajv-formats を登録して format アサーションを有効にすると、不正な値が実際に拒否されます。

結論

JSON Schema は非公式なドキュメント(「このフィールドは数値であるべき」)を、言語・チーム・システムをまたいで一貫して強制できる機械可読な契約に変えます。中心語彙 — typepropertiesrequireditemsenum — は実世界の大多数のユースケースをカバーします。$ref、合成キーワード、format のような高度な機能が残りを担います。

本当に強制したい部分だけを強制する最小スキーマから始めましょう。データへの理解が固まるにつれ制約を段階的に追加します。有効なデータを拒否するスキーマは、スキーマがないより悪いです。

検証しようとしている JSON がスキーマバリデータに届く前に構文エラーを抱えているなら、まずそれを直してください — スキーマバリデータは入力に構文的に正しい JSON を要求します。JSON が綺麗になったら 検証して からスキーマを適用してください。