writing-json-schemas について
writing-json-schemas は、z-schema での検証を想定した、慣用的な JSON Schema の作成を支援するスキルです。構造や制約、合成、参照、ドラフト別の実践方法を扱い、draft-2020-12 を標準の対象とします。
主な機能
- 01draft-2020-12 のスキーマテンプレート
- 02オブジェクトと配列の制約
- 03コンビネーターと条件付きスキーマ
- 04再利用可能な定義と参照
- 05プロパティを厳密に扱う指針
活用シーン
- 01アプリケーションデータのスキーマ定義
- 02allOf と $defs によるスキーマ合成
- 03draft-2020-12 のタプル構文の選択
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ zaggino/ z-schema --skill writing-json-schemasインストール後、Claude Code で/writing-json-schemasを入力して読み込まれたか確認します。
詳細
z-schema 用 JSON Schema の作成
z-schema で検証される、正しく慣用的な JSON Schema を作成します。デフォルトの対象は draft-2020-12 です。
スキーマテンプレート
すべてのスキーマは、$schema 宣言と type から始めます。
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {},
"required": [],
"additionalProperties": false
}
余分なプロパティを拒否する場合は、additionalProperties: false を明示的に設定します。z-schema では、デフォルトで余分なプロパティが許可されます。
オブジェクトスキーマ
必須フィールドを持つ基本的なオブジェクト
{
"type": "object",
"properties": {
"name": { "type": "string", "minLength": 1 },
"email": { "type": "string", "format": "email" },
"age": { "type": "integer", "minimum": 0, "maximum": 150 }
},
"required": ["name", "email"],
"additionalProperties": false
}
ネストされたオブジェクト
{
"type": "object",
"properties": {
"address": {
"type": "object",
"properties": {
"street": { "type": "string" },
"city": { "type": "string" },
"zip": { "type": "string", "pattern": "^\\d{5}(-\\d{4})?$" }
},
"required": ["street", "city"]
}
}
}
動的なプロパティ名
patternProperties を使用して、正規表現でプロパティキーを検証します。
{
"type": "object",
"patternProperties": {
"^x-": { "type": "string" }
},
"additionalProperties": false
}
propertyNames(draft-06 以降)を使用して、すべてのプロパティキー文字列に制約を設定します。
{
"type": "object",
"propertyNames": { "pattern": "^[a-z_]+$" }
}
配列スキーマ
均一な配列
{
"type": "array",
"items": { "type": "string" },
"minItems": 1,
"uniqueItems": true
}
タプルの検証(draft-2020-12)
位置ごとの型には prefixItems を使用し、残りの要素には items を使用します。
{
"type": "array",
"prefixItems": [{ "type": "string" }, { "type": "integer" }],
"items": false
}
items: false は、タプルの位置を超える余分な要素を拒否します。
Contains(draft-06 以降)
一致する項目が少なくとも 1 つあることを必須にします。
{
"type": "array",
"contains": { "type": "string", "const": "admin" }
}
件数制約を設定する場合(draft-2019-09 以降):
{
"type": "array",
"contains": { "type": "integer", "minimum": 10 },
"minContains": 2,
"maxContains": 5
}
文字列の制約
{
"type": "string",
"minLength": 1,
"maxLength": 255,
"pattern": "^[A-Za-z0-9_]+$"
}
フォーマット検証
z-schema には、date、date-time、time、email、idn-email、hostname、idn-hostname、ipv4、ipv6、uri、uri-reference、uri-template、iri、iri-reference、json-pointer、relative-json-pointer、regex、duration、uuid の組み込みフォーマットバリデーターがあります。
{ "type": "string", "format": "date-time" }
フォーマットアサーションは、デフォルトで常に適用されます(formatAssertions: null)。draft-2020-12 でボキャブラリーに応じた動作を有効にするには、バリデーターに formatAssertions: true を設定します。
数値の制約
{
"type": "number",
"minimum": 0,
"maximum": 100,
"multipleOf": 0.01
}
厳密な範囲制約には exclusiveMinimum / exclusiveMaximum を使用します。
{ "type": "integer", "exclusiveMinimum": 0, "exclusiveMaximum": 100 }
コンビネーター
anyOf — 少なくとも 1 つに一致
{
"anyOf": [{ "type": "string" }, { "type": "number" }]
}
oneOf — ちょうど 1 つに一致
{
"oneOf": [
{ "type": "string", "maxLength": 5 },
{ "type": "string", "minLength": 10 }
]
}
allOf — すべてに一致
スキーマの合成に使用します。基本スキーマに追加の制約を組み合わせます。
{
"allOf": [{ "$ref": "#/$defs/base" }, { "properties": { "extra": { "type": "string" } } }]
}
not — 一致してはならない
{ "not": { "type": "null" } }
if / then / else(draft-07 以降)
条件付き検証です。「X の場合は Y を必須にする」というロジックには、複雑な oneOf よりこちらを推奨します。
{
"type": "object",
"properties": {
"type": { "type": "string", "enum": ["personal", "business"] },
"company": { "type": "string" }
},
"if": { "properties": { "type": { "const": "business" } } },
"then": { "required": ["company"] },
"else": {}
}
コンビネーターの使い分け
| シナリオ | 使用するもの |
|---|---|
| 値が複数の型になり得る | anyOf |
| ちょうど 1 つのバリアントに一致する必要がある | oneOf |
| 継承されたスキーマを合成する | allOf |
| 「条件を満たす場合にフィールドを必須にする」 | if/then/else |
| 特定の形状を除外する | not |
条件が単一の識別フィールドの場合は、oneOf よりも if/then/else を推奨します。エラーメッセージがより明確になります。
$ref と $defs を使ったスキーマの再利用
ローカル定義
{
"$defs": {
"address": {
"type": "object",
"properties": {
"street": { "type": "string" },
"city": { "type": "string" }
},
"required": ["street", "city"]
}
},
"type": "object",
"properties": {
"home": { "$ref": "#/$defs/address" },
"work": { "$ref": "#/$defs/address" }
}
}
スキーマ間の参照
スキーマの配列をコンパイルし、ID で参照します。
import ZSchema from 'z-schema';
const schemas = [
{
$id: 'address',
type: 'object',
properties: { city: { type: 'string' } },
required: ['city'],
},
{
$id: 'person',
type: 'object',
properties: {
name: { type: 'string' },
home: { $ref: 'address' },
},
required: ['name'],
},
];
const validator = ZSchema.create();
validator.validateSchema(schemas);
validator.validate({ name: 'Alice', home: { city: 'Paris' } }, 'person');
unevaluatedProperties を使った厳密なスキーマ(draft-2019-09 以降)
スキーマを allOf で組み合わせると、サブスキーマの additionalProperties: false によって、兄弟スキーマで定義されたプロパティがブロックされます。代わりに unevaluatedProperties を使用してください。適用キーワード全体で評価されたすべてのプロパティを追跡できます。
{
"allOf": [
{
"type": "object",
"properties": { "name": { "type": "string" } },
"required": ["name"]
},
{
"type": "object",
"properties": { "age": { "type": "integer" } }
}
],
"unevaluatedProperties": false
}
これにより { "name": "Alice", "age": 30 } は受け入れられますが、{ "name": "Alice", "age": 30, "extra": true } は拒否されます。
スキーマ自体の検証
起動時に必ずスキーマを検証します。
const validator = ZSchema.create();
try {
validator.validateSchema(schema);
} catch (err) {
console.log('Schema errors:', err.details);
}
よくある間違い
additionalPropertiesの指定漏れ:デフォルトでは余分なプロパティが許可されます。拒否するにはadditionalProperties: falseを設定するか、unevaluatedProperties: falseを使用します。allOfとadditionalProperties: falseの併用:兄弟スキーマのプロパティがブロックされます。代わりにトップレベルでunevaluatedProperties: falseを使用します(draft-2019-09 以降)。- draft-2020-12 での配列の
items:タプルの検証にはprefixItemsを使用します。itemsは現在、「残りの項目に対するスキーマ」を意味します。 $schemaの指定漏れ:指定がない場合、z-schema は設定されたデフォルトのドラフトを使用します。対象のドラフトを明示するには$schemaを含めます。definitionsと$defsの違い:どちらも機能しますが、draft-2019-09 以降では$defsが標準形式です。一貫して使用してください。