JSON Schemaとは
JSONが既定の形式を満たしているかを調べたいことは非常によくあります。例えばREST APIで受け取るJSONが既定の名前と型のプロパティを持っているかどうかを調べて持ってなければ即座にエラーを返す、というような場面です。
このようなJSONの形式を定義するためにJSON Schemaという仕様があります。例として以下のようなname
という文字列とage
という数値のプロパティを持つ、人を表すオブジェクトを考えます。
{
"name": "田中太郎",
"age": 20
}
これを表すJSON Schemaは以下のようになります。JSON Schema自体もJSONで記述します。
{
"type": "object",
"properties": {
"name": {
"type": "string"
},
"age": {
"type": "number"
}
}
}
分かりやすいですね。
非直感的な仕様
では試してみます。JSON Schemaの検証はWeb上の https://jsonschema.dev/ 等で簡単に行えます。前節の人オブジェクトのJSON Schemaに対して、以下はちゃんと妥当(valid)になります。
{
"name": "田中太郎",
"age": 20
}
余計なプロパティを加えた以下は?これも妥当になります。
{
"name": "田中太郎",
"age": 20,
"foo": "bar"
}
ではname
を抜くと?なんと妥当になります!
{
"age": 20
}
では、もはや元のオブジェクトの面影もない以下は?これでもなんと妥当です!!
{
"foo": "bar",
"baz": [0, 1, "abc"]
}
ぶっ壊れているように見えますが、これがJSON Schemaの正しい仕様です。
正しいスキーマ
まずJSON Schemaでは、オブジェクトのプロパティに余計なものを許さないためには以下のように"additionalProperties": false
を指定しなければなりません。これで上記(2)と(4)は不適になります。
{
"type": "object",
"properties": {
"name": {
"type": "string"
},
"age": {
"type": "number"
}
},
"additionalProperties": false
}
次に"properties"
内で列挙されたプロパティ、これはそのままではオプショナル扱いになっていてあってもなくても良いがあるなら以下のスキーマを満たさなければならない、という解釈になります。これを必須にするには以下のように"required"
でプロパティ名を列挙します。これで(3)も不適になります。
{
"type": "object",
"properties": {
"name": {
"type": "string"
},
"age": {
"type": "number"
}
},
"additionalProperties": false,
"required": ["name", "age"]
}
残念な仕様
このようにJSON Schemaは記述を追加しないと想像する判定基準よりも実際の判定基準がかなりゆるゆるになっています。
多くの人はプロパティを指定したらそれは必須だと思うんじゃないでしょうか?REST API等、使用場面を考えてもオプショナルなプロパティより必須のプロパティの方が圧倒的に多いと思うんですが。というか最小の記述だと(4)が妥当になってしまう、こんな挙動が求められる場面があるんですかね...
しかもプロパティ全てを必須にしたくば全てのプロパティ名を列挙しなければならず、無駄に記述量が多いです。プロパティが10個あるとか3段ネストしたオブジェクトとかだったらJSON Schemaでスキーマを書く気が失せるでしょう。
なぜこんな不便かつ非直感的な仕様になっているのかとても謎です。知っている人がいたら教えてください。