Skip to content
ブログに戻る
チュートリアル

JSON Schema バリデーション 2026 完全ガイド:Ajv・Python・ブラウザ

Node、Python、ブラウザで JSON Schema による検証を実装。Draft 2020-12 の新機能、実 API 設計パターン、コピペ可能な実用コードを完全網羅。無料で試せる。

12 分で読める

JSON Schema バリデーション:Node・Python・ブラウザで JSON を検証する(2026 年版)

要点:JSON Schema は JSON データに対する契約だ。フィールドの型、必須キー、制約を宣言しておけば、バリデーターが任意の JSON ドキュメントがその契約に従っているかを判定する。 Node では最速のバリデーターである Ajv、Python ではポータブルなスキーマを扱える jsonschema ライブラリ、ブラウザではフォームや設定の即時フィードバックのために Ajv をバンドルするのが定石だ。2026 年に新規プロジェクトを始めるなら、選ぶべきバージョンは Draft 2020-12 である。

これから見せるのは、最小構成の動く例、3 つのランタイムすべてでの一気通貫パターン、そして「バリデーションは通ったのに本番で弾かれる」という不具合を生む現実の落とし穴だ。

JSON Schema とは何か(そして何ではないか)

一文で言うと

JSON Schema とは、他の JSON ドキュメントの形を記述する JSON ドキュメントだ。バリデーターはスキーマとデータを読み込み、適合しているかどうか、または失敗したパスを返す。

最小限ながら役に立つ例:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": { "name": { "type": "string" } },
  "required": ["name"]
}

{"name": "Alice"} は通る。{"age": 30} は失敗する(name がない)。{"name": 42} も失敗する(name が文字列でない)。これがメンタルモデルのすべてだ。

JSON Schema と JSON 構文チェックの違い

別物の問題なのに、よく混同される。

観点JSON 構文チェックJSON Schema バリデーション
何を確認するかこれは正当な JSON ドキュメントか?この JSON は契約に一致するか?
検出できるものカンマ抜け、シングルクォート、コメント型違い、必須フィールド欠落、範囲外の値
ツールJSON.parse()JSON整形ツールAjv、jsonschema(Python)、fastjsonschema
いつ使うか何より先、パースより前パース直後、ビジネスロジックより前

実務では両方やる。ペイロードを JSON整形ツール で整形してパースが通ることを確認したうえで、スキーマを通して契約に一致しているかを検証する。

JSON Schema、JSONPath、JSON Patch、jq、TypeScript の使い分け

5 つのツールがこの問題領域を共有している。判断マトリックスは次のとおり。

ツール答える問い使うべき場面
JSON Schemaこの JSON は期待される構造に一致するか?API 入力、設定ファイル、フォームのペイロード検証
JSONPathこの JSON から値をどう取り出すか?ネストされたフィールドの抽出、一括読み取り
JSON Patch(RFC 6902)A から B への差分をどう記述するか?共同編集、増分同期
jqコマンドラインで JSON をどう処理するか?シェルスクリプト、ログパイプライン、CI チェック
TypeScript の型自分のコードはこの形を正しく使っているか?1 つのコードベース内でのコンパイル時保証

決定的な違いはここにある。JSON Schema は実行時に未知のデータを検証する。TypeScript はコンパイル時に既知のコードを検証する。 TypeScript はサードパーティの Webhook や、ユーザーが貼り付けた JSON にはまったく無力だ。そこに JSON Schema の出番がある。Zod や Pydantic はその中間(コンパイル時の型と実行時バリデーションを両立)に位置するが、これは後で扱う。

JSON Schema と OpenAPI

OpenAPI が JSON Schema を置き換える、というのはよくある誤解だ。実際にはそうではない。OpenAPI はリクエストとレスポンスのボディを記述するために内部で JSON Schema を使い、その上にパス、パラメータ、セキュリティスキーム、サーバー URL を重ねている。スキーマがデータ形状の契約であり、OpenAPI はそれを包む API 契約だ。

観点JSON SchemaOpenAPI
スコープ1 つの JSON ドキュメントの形HTTP API 全体の形
依存関係なし(スキーマは自己完結した JSON)ボディ定義のために JSON Schema を取り込む
バージョン対応Draft 7 / Draft 2019-09 / Draft 2020-12OpenAPI 3.0 は Draft 4 のサブセット、OpenAPI 3.1 は Draft 2020-12 をネイティブ採用
典型的な用途設定ファイル、メッセージのエンベロープ、フォーム検証、単一ペイロードの契約REST API 設計、SDK 生成、モックサーバー、契約テスト
コード生成限定的(quicktype 系のツールが一部あり)成熟したエコシステム(openapi-generatoroapi-codegen、ベンダー製 SDK)
契約管理1 形状につき 1 ファイル、ルーティングなしパス、操作、認証フロー、バージョン付きエンドポイントを 1 つのドキュメントに集約

成果物が単一のドキュメント、つまり Webhook ペイロード、設定ファイル、キューのメッセージ、フォームなどであるなら、素の JSON Schema を選ぶこと。記述すべき HTTP の表面がないので、OpenAPI は過剰になる。

HTTP API を公開し、ドキュメント、SDK 生成、モックサーバー、契約テストを 1 つのドキュメントから駆動したいなら OpenAPI を選ぶこと。スキーマはまず schemas/ ディレクトリ配下に独立した JSON Schema ファイルとして定義し、OpenAPI ドキュメントから $ref で参照するのがコツだ。こうしておくと、API 文脈の外でもスキーマを再利用できる。

バージョンの組み合わせがチームを混乱させる。OpenAPI 3.0 は Draft 4 のサブセットを使うので、3.0 のドキュメント内で prefixItemsunevaluatedProperties といった Draft 2020-12 のキーワードは使えない(ジェネレーターは黙って無視する)。OpenAPI 3.1 は Draft 2020-12 のスーパーセットなので、2020-12 で有効なものはすべて 3.1 でも有効だ。選択の余地があるなら、OpenAPI 3.1 を狙い、どこでも Draft 2020-12 のスキーマで書くこと。

はじめての JSON Schema(5 分でできる)

まず押さえるキーワード

これだけで 8 割をカバーできる:

{
  "type": "object",
  "properties": {
    "id":       { "type": "integer", "minimum": 1 },
    "email":    { "type": "string", "format": "email" },
    "age":      { "type": "integer", "minimum": 0, "maximum": 150 },
    "tags":     { "type": "array", "items": { "type": "string" }, "minItems": 1 },
    "role":     { "enum": ["admin", "editor", "viewer"] },
    "metadata": { "type": "object", "additionalProperties": true }
  },
  "required": ["id", "email"],
  "additionalProperties": false
}

語彙は次のとおり:

  • typestringnumberintegerbooleannullarrayobject
  • properties + required:フィールドを宣言し、必須かどうかを示す
  • enum / const:固定の集合または単一リテラルに制限する
  • minimum / maximum / multipleOf:数値の上下限
  • minLength / maxLength / pattern:文字列長と正規表現
  • minItems / maxItems / uniqueItems:配列の形
  • additionalProperties: false:宣言されていないキーを拒否する(入力契約には常にこれを設定すること)

ユースケース別 JSON Schema 例

上のキーワードは、何を検証するかによって異なる組み合わせで姿を現す。代表的な形をいくつか挙げる。

API リクエストボディ — メールとパスワードを受け取るサインアップエンドポイント:

{
  "type": "object",
  "properties": {
    "email":    { "type": "string", "format": "email" },
    "password": { "type": "string", "minLength": 8, "maxLength": 128 }
  },
  "required": ["email", "password"],
  "additionalProperties": false
}

設定ファイル — レベルを固定セットに縛るロガー設定:

{
  "type": "object",
  "properties": {
    "level":  { "enum": ["debug", "info", "warn", "error"] },
    "output": { "type": "string", "default": "stdout" }
  },
  "required": ["level"],
  "additionalProperties": false
}

条件付きルールのあるフォームペイロード — accountType"business" のときに taxId を必須にする:

{
  "type": "object",
  "properties": {
    "accountType": { "enum": ["personal", "business"] },
    "taxId":       { "type": "string" }
  },
  "if":   { "properties": { "accountType": { "const": "business" } } },
  "then": { "required": ["taxId"] }
}

CSV 行を表す JSON レコード — エクスポートされた注文テーブルの 1 行:

{
  "type": "object",
  "properties": {
    "orderId":   { "type": "string", "pattern": "^ORD-[0-9]{6}$" },
    "orderedOn": { "type": "string", "format": "date" },
    "totalUsd":  { "type": "number", "minimum": 0 }
  },
  "required": ["orderId", "orderedOn", "totalUsd"]
}

Webhook イベントのエンベロープ — oneOftype リテラルで分岐し、各イベントバリアントが固有のペイロード形を持つ:

{
  "oneOf": [
    { "properties": { "type": { "const": "order.created" }, "data": { "$ref": "#/$defs/order" } } },
    { "properties": { "type": { "const": "order.refunded" }, "data": { "$ref": "#/$defs/refund" } } }
  ]
}

実務でチームが書くものは、この 5 例でほぼ網羅できる。最も近いものをコピーしてフィールド名を調整すればよい。キーワードの語彙はそのままだ。

インストール不要で検証する

スキーマとペイロードを ajv.js.orgjsonschemavalidator.net のプレイグラウンドに貼り付ければ、即座に判定が出る。JSON 自体が怪しければ、まず JSON整形ツール を通すといい。

Node.js で Ajv を使って検証する

インストールと 12 行のサンプル

Ajv は最初の compile 時にスキーマを最適化された関数にコンパイルし、それを再利用する。

npm install ajv
import Ajv from "ajv";
const ajv = new Ajv();

const schema = {
  type: "object",
  properties: {
    name: { type: "string" },
    age:  { type: "integer", minimum: 0 }
  },
  required: ["name"]
};

const validate = ajv.compile(schema);
const data = { name: "Alice", age: 30 };

if (!validate(data)) console.log(validate.errors);
else                  console.log("OK");

Draft 2020-12 への切り替え

デフォルトの Ajv コンストラクタは後方互換のため、いまも Draft 7 に固定されている。2020-12 を使うには明示的にオプトインすること:

import Ajv2020 from "ajv/dist/2020";
const ajv = new Ajv2020({ strict: true, allErrors: true });

これで prefixItemsunevaluatedProperties$dynamicRef が使えるようになる。各機能の役割は後述の Draft 2020-12 のセクションを参照。

format バリデーションを有効にする

これは Ajv の癖の中でも、開発者がいちばんよく踏み抜く落とし穴だ。format: "email" はデフォルトでは何もしない。仕様上 format は助言的扱いなので、フォーマットモジュールを別途登録する必要がある。

npm install ajv-formats
import addFormats from "ajv-formats";
addFormats(ajv);  // これで "format": "email" が実際に検証されるようになる

このステップを飛ばすと、format: "email" を要求するスキーマでも {"email": "not-an-email"} が通ってしまう。本番では必ず ajv-formats を入れること。

Express ミドルウェアの実戦投入

ルートごとに 1 つのバリデーターを用意し、起動時にコンパイルしておく:

import express from "express";
import Ajv2020 from "ajv/dist/2020";
import addFormats from "ajv-formats";

const ajv = new Ajv2020({ allErrors: true });
addFormats(ajv);

const validateUser = ajv.compile({
  type: "object",
  properties: {
    email: { type: "string", format: "email" },
    age:   { type: "integer", minimum: 13 }
  },
  required: ["email"],
  additionalProperties: false
});

const app = express();
app.use(express.json());

app.post("/users", (req, res) => {
  if (!validateUser(req.body)) {
    return res.status(400).json({ errors: validateUser.errors });
  }
  // ... ビジネスロジック
  res.status(201).json({ ok: true });
});

唯一、もっとも高くつくミスは、リクエストハンドラーの中で ajv.compile(schema) を呼ぶことだ。コンパイルはモジュールスコープで一度だけ行い、返された関数を使い回す。リクエストごとに再コンパイルすると、スループットが 50 倍以上下がる。

Python で jsonschema を使って検証する

インストールと基本的な使い方

pip install jsonschema
from jsonschema import validate, ValidationError

schema = {
    "type": "object",
    "properties": {
        "name": {"type": "string"},
        "age":  {"type": "integer", "minimum": 0}
    },
    "required": ["name"]
}

try:
    validate(instance={"name": "Alice", "age": 30}, schema=schema)
    print("OK")
except ValidationError as e:
    print("FAIL:", e.message, "at", list(e.absolute_path))

Draft202012Validator で全エラーを集める

validate() は最初のエラーで例外を投げる。フォームのレスポンスのように全問題を一度に列挙したいなら、iter_errors を使う:

from jsonschema import Draft202012Validator

validator = Draft202012Validator(schema)
errors = sorted(validator.iter_errors(instance), key=lambda e: e.path)
for err in errors:
    print(f"  - {'/'.join(map(str, err.absolute_path))}: {err.message}")

これでユーザーは何度もやり直すことなく、すべてを一度で修正できる。

jsonschema と Pydantic の使い分け

優れた Python ライブラリが 2 つあるが、解こうとしている問題が違う。

観点jsonschemaPydantic v2
スキーマ形式JSON の dict(スキーマがデータそのもの)型ヒント付きの Python クラス
性能インタプリタ動作、Pydantic より約 10〜100 倍遅いRust コア、エコシステム最速
言語横断のポータビリティあり(同じスキーマが JS、Go、Rust でも動く)なし(Python 専用)
FastAPI/ネイティブモデル統合手動変換標準搭載
Draft 2020-12 のキーワード($dynamicRef など)完全対応部分対応

本番運用で通用する原則はこうだ。言語横断の契約(OpenAPI、公開 API、Webhook)には jsonschema、内部 Python サービスには Pydantic を使う。 両方を併用しているチームも多い。ゲートウェイ層で jsonschema が契約を強制し、アプリケーション層で Pydantic が型付きビジネスロジックを担う構成だ。スキーマは可搬な成果物であり、Ajv に渡すものとまったく同じになる。

ブラウザで検証する

そもそもクライアント側で検証する理由

重要度の高い順に 3 つある。第一に UX で、ユーザーの入力に合わせて即座にフィードバックが返る方が、サーバー往復より速い。第二に帯域で、明らかなミスはブラウザから出ていかない。第三にセキュリティ衛生で、バックエンドへのゴミの量を減らせる(サーバー検証の代替にはならない)。

クライアント側だけのバリデーションを信用してはいけない。サーバー側でも必ずもう一度検証すること。

Ajv をブラウザ向けにバンドルする

npm install ajv ajv-formats
import Ajv2020 from "ajv/dist/2020";
import addFormats from "ajv-formats";

const ajv = new Ajv2020({ allErrors: true });
addFormats(ajv);

export const validateForm = ajv.compile({
  type: "object",
  properties: {
    email:    { type: "string", format: "email" },
    password: { type: "string", minLength: 8 }
  },
  required: ["email", "password"]
});

バンドルサイズは gzip 後でおよそ 30 KB 増える。無視できない大きさだが、十分許容範囲に収まる。サーバーとクライアントで 1 つのスキーマ定義を共有したいチームは、迷わず Ajv を採用する。

軽量な代替:Zod と Valibot

JSON Schema エコシステムを必要とせず、すでに TypeScript で書いているなら、TS ネイティブのバリデーターの方がバンドルが小さく、型推論もより厳密にできる:

import { z } from "zod";
const UserSchema = z.object({
  email: z.string().email(),
  password: z.string().min(8)
});
const result = UserSchema.safeParse(data);
if (!result.success) console.log(result.error.issues);

Valibot は同等の API で gzip 後およそ 3 KB に収まる。バンドルサイズが最優先なら Valibot を選ぶといい。注意点として、どちらも JSON Schema を生成しない。バックエンド、サードパーティクライアント、OpenAPI ジェネレーターと共有する単一の真実の源が必要なら Ajv に留まること。すべて自前の TypeScript で完結するなら、Zod や Valibot の方が扱いやすい。

Draft 2020-12 で増えたもの

prefixItems でタプル検証

Draft 7 はタプルを items: []additionalItems で表現していた。Draft 2020-12 はこれをきれいに分離した:

{
  "type": "array",
  "prefixItems": [
    { "type": "string" },
    { "type": "number" }
  ],
  "items": false
}

["x", 42] は通る。["x", 42, "extra"] は失敗する。スキーマを読めば、それが何をするかがそのままわかる。

unevaluatedProperties で合成スキーマに対処する

allOfoneOf を使うチーム全員を悩ませる地味な不具合がある。additionalProperties: false は、それが現れている直近の階層しか見ない。allOf の中の兄弟サブスキーマは、好きにプロパティを宣言できる。Draft 2020-12 の解決策が unevaluatedProperties: false だ:

{
  "allOf": [
    { "$ref": "#/$defs/base" }
  ],
  "unevaluatedProperties": false
}

これにより、どの分岐でも評価されなかったプロパティはすべて拒否される。多くの開発者が additionalProperties: false に期待していたのは、まさにこの挙動である。

$dynamicRef で再帰スキーマ

Draft 7 で再帰的なツリースキーマを書こうとして苦労した経験がある人なら、その面倒さは身に染みているはずだ。$dynamicRef$dynamicAnchor の組み合わせがそれをすっきりさせる:

{
  "$dynamicAnchor": "node",
  "type": "object",
  "properties": {
    "value": { "type": "string" },
    "children": { "type": "array", "items": { "$dynamicRef": "#node" } }
  }
}

再帰は宣言的に書けて、$id の書き換えなしに子孫からオーバーライドできる。

Draft 7 と 2020-12 の選び方

  • 新規プロジェクト、モダンなツールチェーン → Draft 2020-12
  • OpenAPI 3.1 を作る/使う → 2020-12 がネイティブの方言
  • OpenAPI 3.0 や旧サービスとやりとりする → Draft 4(OpenAPI 3.0 は Draft 4 のサブセットを使う。方言を混ぜないこと)
  • 幅広いバリデーター互換性が必要(Postman、古い CI ツール) → 交換フォーマットとしては Draft 7 がいまだに最も安全

モダンなバリデーター(Ajv、Python の jsonschemajsonschema-rs、Java の networknt/json-schema-validator)はすべて、現時点で 2020-12 をサポートしている。

実戦パターン

API 入力のバリデーション

先ほどの Express ミドルウェアが本番で見られる典型形だ。これに加えて 2 つの実践がある。スキーマはすべてリポジトリルートの schemas/ ディレクトリにまとめる、そして ajv test(または Python 同等品)でスキーマ自体を JSON Schema のメタスキーマに照らして検証する CI ステップを追加する。

設定ファイル

Visual Studio Code は SchemaStore と統合されており、package.jsontsconfig.json など多数の設定ファイルで自動補完とインライン検証が効く。自分の設定にも $schema フィールドを追加すれば、エディタユーザーは同じ恩恵を受けられる。

CI のテストフィクスチャ

テストフィクスチャは腐る。誰かがモデルを更新し、フィクスチャは古い形のまま、変更されたフィールドにアサーションが触れていないせいでテストはパスし続ける。これをアサーション実行前のスキーマチェックで捕まえよう:

import { glob } from "glob";
const files = await glob("__tests__/fixtures/*.json");
for (const f of files) {
  const data = JSON.parse(await fs.readFile(f, "utf8"));
  if (!validate(data)) throw new Error(`${f}: ${ajv.errorsText(validate.errors)}`);
}

スキーマチェックが発火したら、次にやるのは構造の差分確認だ。フィクスチャを JSON 差分ツール に投入し、最新の本番サンプルと比べてどこがズレたかを見る。タイムスタンプや ID が差分の大半を占めるなら、JSON 差分ガイドのスナップショット用パス無視パターンを適用してノイズと信号を分離するとよい。

Webhook ペイロード(Stripe、GitHub)

サードパーティの Webhook は、JSON Schema を適用する価値が最も高い場所のひとつだ。Webhook は契約であり、提供元はそれを変えうる。変えた瞬間に気づきたい。Stripe や GitHub は OpenAPI の記述を公開しており、そこから JSON Schema を抽出できる。受信イベントを検証していれば、破壊的アップグレードが静かに状態を壊すかわりに監視を点灯させる。

スキーマ駆動のフォームバリデーション

React Hook Form には @hookform/resolvers/ajv アダプタがあり、Vue の VeeValidate にも同等の Ajv プラグインがある。どちらもフォームの描画、エラーメッセージ、送信時バリデーションを 1 つの JSON Schema で駆動する。スキーマが単一の真実の源となり、UI はそのルールを継承する。

親しみやすいエラーメッセージ

デフォルトが粗削りな理由

Ajv は標準で #/properties/email format must match "email" のようなエラーを出す。400 をデバッグするエンジニアにはちょうどいい。だがチェックアウトフォームを埋めるユーザーには役に立たない。

ajv-errors でカスタムメッセージ

npm install ajv-errors
import ajvErrors from "ajv-errors";
ajvErrors(ajv);

const schema = {
  type: "object",
  properties: { email: { type: "string", format: "email" } },
  required: ["email"],
  errorMessage: {
    properties: { email: "有効なメールアドレスを入力してください" },
    required: { email: "メールアドレスは必須です" }
  }
};

errorMessage キーワードはスキーマの中に留まるので、バリデーションルールとユーザー向けのコピーが一緒に移動する。

ajv-i18n で翻訳済みエラー

ajv-i18n はデフォルトメッセージの翻訳を 30 以上の言語で提供している。起動時に 1 行加えるだけで、バリデーターはスペイン語、フランス語、日本語など、提供したいロケールで話してくれる。errorMessage のオーバーライドがすべての制約をカバーしきれない場合のフォールバックとして便利だ。

スキーマパスをフォームフィールドに対応づける

Ajv の各エラーには /users/0/email のような instancePath が含まれる。多くのフォームライブラリは users[0].email のようなドット区切りパスを期待する。ワンライナーで済む:

const fieldPath = error.instancePath.replace(/^\//, "").replace(/\//g, ".");

Python の jsonschema では error.absolute_path に同じ情報がある。. で結合すれば同じ効果になる。

バリデーションを通り抜けて本番でクラッシュさせる 5 つの罠

1. format はデフォルトで助言的

ajv-formatsaddFormats(ajv) がなければ、すべての format キーワードは何もしないのと同じだ。{"format": "email"}"not-an-email" を受け入れてしまう。本番ではフォーマットパッケージを必ず入れること。

2. additionalProperties のデフォルトは true

additionalProperties: false を付けないと、スキーマは宣言されていないあらゆるフィールドを受け入れる。クライアントは検証をすり抜ける追加フィールドを送り込める。入力契約では additionalProperties: false をデフォルトにし、必要に応じて意図的に緩めるのが筋だ。

3. additionalProperties は合成しない

allOfoneOfanyOf の中では、additionalProperties: false は自分の階層しか見ない。兄弟のサブスキーマがすり抜ける。Draft 2020-12 の解決策は unevaluatedProperties: false だ。

4. リモート $ref は本番リスク

$ref: "https://example.com/schema.json" を書くと、Ajv は最初のコンパイル時にネットワーク越しにフェッチに行く。これはレイテンシ、リモートホストが固まったときの DoS 露出、MITM の攻撃面を意味する。$ref のターゲットはすべてインライン化するか、ビルド時にディスクから読み込むこと。

5. 生成されたスキーマは実データから乖離する

quicktype や typescript-json-schema のようなツールは、既存の型からスキーマを生成してくれる。ただし出力は通常、緩すぎる。すべてのフィールドが任意で、additionalProperties は開放されている。生成されたスキーマは下書きとして扱い、手で締めて、実本番サンプルをスキーマで検証する(およびその逆を行う)CI を回し、乖離が早期に表面化するようにするとよい。

性能:数値と経験則

  • Ajv(Node.js):コンパイル済みバリデーターは 1 件のチェックを 1 マイクロ秒未満でこなす。本番品質の JS バリデーターでは最速。
  • jsonschema(Python):インタプリタ動作で、Pydantic より 10〜100 倍遅い。ボトルネックになったら fastjsonschema に差し替えること。Python コードを生成し、Ajv 並みの性能に届く。
  • Rust と Go:jsonschema-rsxeipuuv/gojsonschema は、ゲートウェイ層で Ajv からさらに 2〜5 倍速くなる。
  • 最大の効果が出る一手はプリコンパイルだ。ajv.compile(schema) をモジュールロード時に一度だけ呼び、返ってきたバリデーターをリクエストごとに使い回す。リクエストごとに再コンパイルすると、スループットが 50 倍以上落ちる。

よくある質問

JSON Schema バリデーションをやさしく言うと?

JSON Schema バリデーションとは、JSON ドキュメントが契約に従っているかを確認することだ。契約(スキーマ)自体も JSON で、型、必須フィールド、制約を宣言する。バリデーターはスキーマとデータを読み、「合格」または「失敗したパスとその理由」を返す。

オンラインで JSON をスキーマに照らして検証するには?

スキーマとデータを ajv.js.org のプレイグラウンドや jsonschemavalidator.net に貼り付ければ、即座に判定が出る。JSON が壊れていそうなら、まず JSON整形ツール で整えるとよい。どちらもブラウザで動き、アップロードは不要だ。

2026 年で最速の JSON Schema バリデーターは?

Node では、プリコンパイル済みの Ajv が 1 件のチェックを 1 マイクロ秒未満でこなす。Python では、fastjsonschema がコードを生成して Ajv 級のスループットに届く。ゲートウェイ層では jsonschema-rs(Rust)と gojsonschema(Go)が Ajv より 2〜5 倍速い。何を選ぶにせよ、一度プリコンパイルして使い回すこと。

JSON Schema と TypeScript の型の違いは?

TypeScript はあなたが書いたコードをコンパイル時にチェックする。JSON Schema は未知の JSON を実行時にチェックする。HTTP レスポンス、ファイル、ユーザーが貼り付けた内容として届く JSON は TypeScript には見えない。そのために JSON Schema がある。

Draft 2020-12 と Draft 7、どちらを選ぶべきか?

2026 年に新規プロジェクトを始めるなら Draft 2020-12 を選ぶこと。prefixItemsunevaluatedProperties$dynamicRef は実問題を解決する。OpenAPI 3.1 は 2020-12 をネイティブで使う。Draft 7 に留まるべきなのは、Postman 互換性や古いサービスのためだけだ。OpenAPI 3.0 は Draft 4 のサブセットを使うので、方言を混ぜないこと。

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

3 つの選択肢がある。サンプルを quicktype.io か jsonschema.net に貼る。コマンドラインで npx genson-js または pip install genson && genson sample.json を実行する。あるいは手で書く。自動生成されたスキーマはフィールドが任意、additionalProperties: true と緩すぎるので、契約として扱う前に必ず締めること。

JSON Schema は OpenAPI を置き換えられる?

置き換えられない。OpenAPI はリクエスト/レスポンスボディの記述に内部で JSON Schema を使い、その上にパス、セキュリティスキーム、パラメータ、サーバー URL を加えている。両者は組み合わさる。スキーマを書き、OpenAPI ドキュメントから参照すれば、完全な API 契約が手に入る。

JSON Schema は JSONPath や jq と同じ?

別の問題を解いている。JSON Schema は構造を検証する(「この JSON は契約に一致するか?」)。JSONPath と jq は値を抽出する(「Running フェーズの全 Pod 名」)。検証はスキーマで、クエリは JSONPath か jq でやるという棲み分けだ。

Ajv のバリデーションは通ったのに本番でデータが弾かれるのはなぜ?

ほぼすべてのケースは 3 つの原因に集約される。ajv-formats を忘れていたために format: "email" が一切検証されていなかった、additionalProperties: false を省略したために余計なクライアントフィールドがすり抜けた、allOfoneOf の中で additionalProperties: false を使って合成されないことに気づいた。最後の場合は unevaluatedProperties: false に切り替えること。

エンドユーザー向けに JSON Schema のエラーメッセージをカスタマイズできる?

できる。Node では ajv-errors を入れてスキーマ内に errorMessage を埋め込み、ajv-i18n で 30 以上のロケールに翻訳する。Python では jsonschema が各エラーオブジェクトにバリデーション文脈をすべて公開しているので、エラー型とパスをデザインシステムが使う任意のコピーに対応づけられる。