- ブラウザURL入力
- DNS名前→IP
- ルータ経路選択
- ロードバランサ振り分け
- Nginx受け口
- アプリ処理今ここ
- DB保存
依頼文を機械が読める条件に書き直すと、曖昧さは消えるのか。アプリの入口で JSON を検査するスキーマで確かめます。
まず結論
このコースの芯は、仕様に書かなかったことは消えずに残り、実装した誰か(人でも AI でも)が黙って決める、でした。機械が確かめる条件にしても同じです。JSON Schema では、書かなかったキーワードは何も拒否しません。
「数量は1〜99の整数・必須、メモは3文字以内」と書いたスキーマは、打ち間違えたキー qtty も admin:true も通しました。書いた「3文字」も、JS・スキーマ・画面の見た目で数え方が違います。
数量 qty を「1〜99の整数・必須」とだけ書いたスキーマに `{"qty":1,"qtty":5,"admin":true}` を渡すと、検証器 ajv 8.20.0 はどう判定したでしょうか。
当てることが目的ではありません。外した予測ほど記憶に残るので、直感で選んでください。
書かなかったキーワードは、偽を出さない
依頼文を「ちゃんと」書いても、読んだ側が黙って選ぶ余地は残ります。では、機械が確かめる条件として書けばどうでしょう。人も AI も解釈せず、検証器が機械的に判定します。
JSON Schema は、届いた JSON がどんな形であるべきかをキーワードで書きます。現行の 2020-12 は json-schema.org が配る Internet-Draft(draft-bhutton-json-schema-01、2022-06-16)です。その Core §7.3 はこう定めます。
A missing keyword MUST NOT produce a false assertion result, MUST NOT produce annotation results, and MUST NOT cause any other schema to be evaluated as part of its own behavioral definition.
書かれていないキーワードは、偽(不合格)を出してはならない。検証は、書いた条件ごとに拒否するかを判定する作業で、書かなかった条件は何も拒否しません。 個別のキーワードも省略時を定めています。
additionalProperties(Core §10.3.2.3):Omitting this keyword has the same assertion behavior as an empty schema.空スキーマは何でも通す。required(Validation §6.5.3):Omitting this keyword has the same behavior as an empty array.求めるキーが無い。
- ・「書かなかった」は「禁止した」ではなく「何でも可と決めた」と同じ結果になる
- ・その結果を選んだのは、スキーマを書いた人の沈黙である
打ち間違えたキーが、検証を通る
注文の数量 qty と、任意のメモ note を受け取るスキーマです。検証器は ajv 8.20.0 の Ajv2020(npm i ajv@8 で導入。eslint などが間接に入れる ajv 6 系は draft-07 までで、2020-12 用の Ajv2020 を持ちません)。
{"type": "object","properties": {"qty": { "type": "integer", "minimum": 1, "maximum": 99 },"note": { "type": "string", "maxLength": 3 }},"required": ["qty"]}
{"qty":0} invalid: /qty must be >= 1{"qty":100} invalid: /qty must be <= 99{"qty":1.5} invalid: /qty must be integer{"qty":"1"} invalid: /qty must be integer{"qty":1,"note":"abcd"} invalid: /note must NOT have more than 3 characters{"qty":1,"qtty":5,"admin":true} valid{} invalid: must have required property 'qty'
書いた条件はよく働いています。0 も 100 も 1.5 も文字列の "1" も拒否し、{} は required で止めました。それでも6行目は valid です。qtty が qty の打ち間違いだとしても、admin が権限を上げようとする値だとしても、スキーマはそれについて何も書いていません。 受け取ったアプリが admin を読むかどうかは、アプリを書いた誰かが決めます。
additionalProperties: false を足すと結果が変わります。
closed {"qty":1,"qtty":5,"admin":true} invalid: must NOT have additional properties {"additionalProperty":"qtty"}; must NOT have additional properties {"additionalProperty":"admin"}closed {"qty":1,"note":"ok"} valid
どちらが正しいかは、スキーマの規則からは出てきません。知らないキーを拒否するか通すかは決定で、書かなければ「通す」が選ばれます。
`additionalProperties` を書いていないスキーマについて、「スキーマ検証を入れたので、打ち間違えたキーは弾かれる」という理解は妥当ですか。
書いた条件にも、書かなかった読み方が残る
では、書いたキーワードなら意味は1つに決まるのか。maxLength: 3 の「長さ」を Validation §6.3.1 はこう定義します。
The length of a string instance is defined as the number of its characters as defined by RFC 8259 [RFC8259].
参照先の RFC 8259 に character の定義はありません。§1 に A string is a sequence of zero or more Unicode characters [UNICODE]. とあるだけです。単位を言い切れる根拠は Core §4.2.1 の string の定義 A string of Unicode code points, from the JSON "string" value で、コードポイントの数と読むのが Core と整合します。
一方、JS の length は ECMA-262 §6.1.4 が The length of a String is the number of elements (i.e., 16-bit values) within it. と定めます。画面で人が数えるのは、見た目の1文字(書記素)です。依頼文の「3文字以内」は、このどれとも読めます。
「3文字」を3通りに数える
文字列 UTF-16の単位 コードポイント 書記素 ajv"abc" 3 3 3 valid"😀😀😀" 6 3 3 valid"👨👩👧" 8 5 1 invalid"か" + U+3099 2 2 1 valid
😀😀😀 は JS なら6文字で、JS 側で length <= 3 と確かめる実装なら拒否されます。👨👩👧 は画面では1文字ですが、ajv は拒否しました。実測では ajv の既定はコードポイントで数えており、これは仕様どおりで不具合ではありません。どの数え方が依頼者の意図かは、依頼文に書かれていなければ実装した誰かが選びます。
type: "integer" も同じです。Validation §6.1.1 は integer を any number with a zero fractional part とします。JSON.parse('{"qty":2.0}') を通すと { qty: 2 } になり、ajv は valid と判定しました。「整数」が「小数点を書かない」の意味なら、それはスキーマのどこにもありません。
ajv 8.20.0 で `maxLength: 3` に対し、😀😀😀 は valid、見た目1文字の 👨👩👧 は invalid でした。両方の結果と合う数え方はどれですか。
JSON Schema の additionalProperties
書かなければ空スキーマと同じで、qtty も admin も通りました。false と宣言したときだけ、挙げなかったキーが拒否されます。
宣言 {"qty":1,"qtty":5,"admin":true}additionalProperties なし validadditionalProperties: false invalid(qtty と admin)
データベースの UNIQUE(第17期を読んだなら)
第17期「一意性は、宣言したときだけ存在する」では、2つの接続で同時に「確かめてから入れる」と、制約を宣言していない表に同じ行が2つ入り、UNIQUE を宣言すると後から来た側が拒否されました。データベースは一つであるべきだと知らされていなければ止めません。
JSON の検証器とデータベース。仕組みはまったく違いますが、共通していることは何でしょうか。
「何を書いたときに、拒否が起きたか」を両方で比べてください。
この章のまとめ
- JSON Schema 2020-12 Core §7.3: 書かなかったキーワードは偽を出さない。 スキーマは書いた条件の分だけ拒否する。
additionalPropertiesの省略は空スキーマ、requiredの省略は空配列。打ち間違えたキーも、書かなければ通る。maxLengthの単位は Core の定義からコードポイント。JS の length(UTF-16 の単位)とも、画面の見た目とも違う。- integer は「小数部がゼロの数」。JSON テキストの 2.0 は通る。
- データベースの UNIQUE と同じく、機械が確かめる条件も、宣言した分しか守らない。 書かなかった分は、実装した誰かが黙って決める。
自分の現場で確かめる
読んだだけでは身につきません。手元の環境で実際に見てみてください。
理解度チェック
読んだ直後は「分かった」と感じやすいものです。実際に解いて確かめてください。
properties に qty を書き、`required` を書かなかったスキーマに `{}` を渡すと、2020-12 の Validation 文書に照らしてどうなりますか。
用語集(5語)
- JSON Schema 2020-12
- JSON の形を条件として書く語彙。json-schema.org が配る現行版は Internet-Draft(draft-bhutton-json-schema-01 ほか、2022-06-16)で、RFC ではない。
- additionalProperties
- properties 等に挙げなかったキーの値に掛ける条件。省略すると空スキーマと同じで何でも通る。false にすると、挙げなかったキーはすべて拒否される。
- コードポイント
- Unicode が文字に割り当てた番号。😀 は1つ、👨👩👧 は5つ(3人の絵文字と、つなぐ記号2つ)。
- 書記素
- 人が1文字と見る単位。「か」に結合用の濁点 U+3099 を続けた並びは、コードポイント2つで書記素1つ。
- UTF-16 の単位
- JS の length が数える16ビットの値。😀 は2単位なので `"😀😀😀".length` は6。
出典(一次ソース)
- JSON Schema: A Media Type for Describing JSON Documents(draft-bhutton-json-schema-01, 2022-06-16)§4.2.1・§7.3・§10.3.2.3 ↗
- JSON Schema Validation(draft-bhutton-json-schema-validation-01, 2022-06-16)§6.1.1・§6.3.1・§6.5.3 ↗
- RFC 8259 — The JavaScript Object Notation (JSON) Data Interchange Format §1 ↗
- ECMA-262(ES2027 ドラフト、2026-09-24 取得)§6.1.4 The String Type ↗
- Ajv JSON schema validator(実演は 8.20.0 / Node v24.21.0 / macOS 26.5.2、2026-09-24) ↗