• ..

Web Service

    構造

    {
      "$schema": "http://json-schema.org/draft-07/schema#",
      "title": "ほげほげ",
      "type": "object",
      "properties": {},
      "required": []
    }

    $schema

    これには今現在(2019-04)最新のhttp://json-schema.org/draft-07/schema#バージョンを指定します。

    title

    そのスキーマの説明。

    type

    ルートに置かれるtypeは、その JSON のルートがどんな要素かを指定します。objectarrayが指定できます。

    properties

    type:objectな時、どんなプロパティがあるかを指定していきます。例えば以下は、fooプロパティが置けるという意味になります。

    {
      "properties": {
        "foo": {
          "description": "説明(オプション)",
          "type": "string"
        }
      }
    }

    この各プロパティのタイプには

    • string
    • number
    • object
    • array
    • boolean
    • null があります。

    required

    type:objectな時、ここに羅列したプロパティは必ず設定しなければならないという意味になります。以下はfooを必須にする例です。

    {
      "properties": {"foo": {}},
      "required": ["foo"]
    }

    共通部分の切り出し

    definitionsというブロックに指定した値はpropertiesの各設定で使い回すことができます。

    例えば以下は URL の設定を期待するプロパティが2つあるスキーマの例です。(ルートのtitleなどは省略)
    ちなみにpatternは文字列のパターンにマッチしたものだけに絞ることができるプロパティです。

    {
      "type": "object",
      "properties": {
        "link": {
          "type": "string",
          "pattern": "^(http(s)?://)?[\\w.-]+(?:\\.[\\w\\.-]+)+[\\w\\-\\._~:/?#[\\]@!\\$&'\\(\\)\\*\\+,;=.]+$"
        },
        "iconLink": {
          "type": "string",
          "pattern": "^(http(s)?://)?[\\w.-]+(?:\\.[\\w\\.-]+)+[\\w\\-\\._~:/?#[\\]@!\\$&'\\(\\)\\*\\+,;=.]+$"
        },
      }
    }

    ただこのパターンを覚えるのは厳しいので、同じようなプロパティを追加しなければいけなくなった時にコピペして使う他ありません。ただそれも、スキーマの定義部分がぱっと見複雑そうな構造になってしまいます。

    このような場合はdefinitionsプロパティをルートに定義します。

    definitions

    例えばこのようにしてみます。

    {
      "definitions": {
            "pattern": {
                "url": {
                    "type": "string",
                    "pattern": "^(http(s)?://)?[\\w.-]+(?:\\.[\\w\\.-]+)+[\\w\\-\\._~:/?#[\\]@!\\$&'\\(\\)\\*\\+,;=.]+$"
                }
            },
      }
    }

    このdefinitions以下で定義した値は、propertiesの各プロパティでベース(JavaScriptでのObject.assign)のように使うことができます。それには$refというプロパティに#(ルート)からのパスを指定するだけです。子のパス参照は.ではなく/を使います。

    {
      "type": "object",
      "properties": {
        "link": {
          "$ref": "#/definitions/pattern/url"
        },
        "iconLink": {
          "$ref": "#/definitions/pattern/url",
          "description": "`description`設定をこちらだけ追加する"
        },
      }
    }

    これで先ほどよりはスッキリ記述することができました。

    $id を使ってさらに短く

    definitions$idを指定すると#/definitions/path/toと長いパスを置かなくても、$refにその名前を設定するだけで同じ意味になります。

    {
      "definitions": {
            "pattern": {
                "url": {
                    "$id": "#urlPattern",
                    "type": "string",
                    "pattern": "^(http(s)?://)?[\\w.-]+(?:\\.[\\w\\.-]+)+[\\w\\-\\._~:/?#[\\]@!\\$&'\\(\\)\\*\\+,;=.]+$"
                }
            }
        }
    }

    使うと

    {
      "type": "object",
      "properties": {
        "link": {
          "$ref": "#urlPattern"
        },
      }
    }

    別ファイルの definitions を使う

    例えば.jsonファイルが大きくなってきたので同じ階層にdefinitions.jsonというファイルを作りdefinitionsプロパティを分割したとします。その時は#/の前にその読み込みたいファイルまでの相対パスを書いて上げると#/以降のパスは分割先のパスを見るようになります。

    {
      "type": "object",
      "properties": {
        "link": {
          "$ref": "./definitions.json#/definitions/pattern/url"
        },
      }
    }

    文字列の設定

    typestringを指定したものは、文字列のプロパティ値を期待する事になります。ただ文字列という情報だけでは範囲が広すぎる場合、それを狭める為のオプションもいくつか用意されています。

    文字列の長さの制限

    minLengthmaxLengthオプションが用意されています。どちらも設定するのは数値で、minLengthの数値以上maxLengthの数値以下の文字列に絞り込むことができます。

    {
      "type": "string",
      "minLength": 2,
      "maxLength": 5
    }

    この設定はabcは正しい、abcdefは間違いだと判断されます。

    文字列のパターンで制限

    例えば URL を期待している箇所に適当な文字列を置かれても困るので、 URL のパターンを設定することでそれにマッチしない場合は誤りであることを伝えることができます。これにはpatternオプションが用意されています。pattern内では\wなどの\\\とエスケープする必要があります。

    以下にいくつか絞る例を上げてみます。

    URL

    https://...みたいな文字列ですね。

    {
      "type": "string",
      "pattern": "^(http(s)?://)?[\\w.-]+(?:\\.[\\w\\.-]+)+[\\w\\-\\._~:/?#[\\]@!\\$&'\\(\\)\\*\\+,;=.]+$"
    }

    Color

    こちらは#fff#000000のような文字列に絞れます。

    {
      "type": "string",
      "pattern": "^#([0-9a-fA-F]{3}){1,2}$"
    }

    特定のワードに限定

    これは別にstringだけのオプションではないですが、enumオプションがあります。type:stringの場合、これは文字列の配列を指定できます。以下に例を置きます。

    {
      "type": "string",
      "enum": [
        "foo",
        "bar",
        "baz"
      ]
    }

    この設定で上のプロパティはfoobar, bazのどれかを置いてほしいという意味になります。

    ここでexamplesというオプションも似たような動作になります。ただし、enumは「必ずどれかを置く」というのを期待しますがexamplesの場合は、単に「例えばこういうのが置けますよ」と提示するだけです。

    {
      "type": "string",
      "examples": [
        "foo",
        "bar",
        "baz"
      ]
    }

    デフォルト値

    defaultを使うと初期値を設定できます。

    {
      "type": "string",
      "default": "foo"
    }

    数値の設定

    type:numberと設定することで数値を期待しているという意味にできます。そして数値にもさらに期待値を絞り込むようなオプションがいくつかあります。

    ある数値の倍数だけに制限

    multipleOf:<number><number>の倍数だけを期待しているという意味にできます。以下は3の倍数だけを期待するという意味になります。

    {
      "type": "number",
      "multipleOf": 3
    }

    範囲

    • 以上
    • 大きい
    • 以下
    • 小さい

    数値を期待するという意味にできるオプションがあります。ちょっと順番が変わります。

    以上

    minimumを置きます。

    {
      "type": "number",
      "minimum": 10
    }

    これは、「最小値はこれ以上にしてくださいね」という感じです。

    大きい

    minimumに合わせてexclusiveMinimum:falseを設定すると、「最小値は除外で」という意味にできます。

    {
      "type": "number",
      "minimum": 10,
      "exclusiveMinimum": true
    }

    これは、11からの数値を期待します。

    以下

    maximumを置きます。

    {
      "type": "number",
      "maximum": 40
    }

    これは、「最大値はこれ以下にしてくださいね」という感じです。

    小さい

    maximumに合わせてexclusiveMaximum:falseを設定すると、「最大値は除外で」という意味にできます。

    {
      "type": "number",
      "maximum": 40,
      "exclusiveMaximum": true
    }

    これは、39以下の数値を期待します。

    組み合わせ

    もちろんこれらは組み合わせて使えます。以下は10 <= num <= 40な数値を期待する例です。

    {
      "type": "number",
      "minimum": 10,
      "maximum": 40,
    }

    特定の数値のどれか

    数値でもenumを使うことで、特定の数値のどれかを期待するという意味にできます。以下は3111002を期待するという意味になります。

    {
      "type": "number",
      "enum": [
        3,
        11,
        1002
      ]
    }

    真偽値の設定

    type:booleanを置くとtruefalseを期待するという意味になります。

    デフォルト値

    defaultオプションを置くと、VScodeなどではプロパティ名を確定した時点でこの値も置かれるようになります。

    {
      "type": "boolean",
      "default": false
    }

    オブジェクトの設定

    type:objectを置くとオブジェクトを期待するという意味になります。期待するプロパティやその値を絞り込むオプションがいくつかあります。

    特定のプロパティ時の期待値の設定

    propertiesを置き、その中でそれぞれのプロパティ名とその名前の時のスキーマを設定できます。

    {
      "type": "object",
      "properties": {
        "foo": {
          "type": "string"
        },
        "bar": {
          "type": "number"
        },
      }
    }

    これは以下のような意味になります。

    • まず、オブジェクトです
    • そのオブジェクトがfooを持つならそれは文字列です
    • そのオブジェクトがbarを持つならそれは文字列です
    • そのオブジェクトがbazを持つならそれは型は何でもいいです

    なので以下は通ります。

    {
      "foo": "foo",
      "baz": false
    }

    厳密なプロパティ

    additionalProperties:falsepropertiesと共に置くことで、完全にproperitesのプロパティ構造以外のプロパティは禁止されるようになります。つまり、先程は設定できたbazは駄目になるという事です。

    {
      "type": "object",
      "properties": {
        "foo": {
          "type": "string"
        },
        "bar": {
          "type": "number"
        },
      },
      "additionalProperties": false
    }

    やや厳しく

    additionalProperties:<設定>propertiesに置いていないプロパティの期待する型を絞ることができます。例えば以下はfooプロパティ以外の値はnumberを期待するスキーマの例です。

    {
      "type": "object",
      "properties": {
        "foo": {
          "type": "string"
        },
      },
      "additionalProperties": {
        "type": "number"
      }
    }

    絶対定義してほしいプロパティ

    reuiqred:<properties>propertiesと共に置くとここで羅列したプロパティ名は必須項目という意味になります。例えば以下は「fooは絶対に定義してね」という意味になります。

    {
      "type": "object",
      "properties": {
        "foo": {
          "type": "string"
        },
        "bar": {
          "type": "number"
        },
      },
      "required": [
        "foo"
      ]
    }

    依存関係のプロパティ

    先程から何度か「reuiqred:<properties>propertiesと共に置くと...」のように言っていますが、このように「requiredpropertiesが存在するなら設定できる」ような関係を設定できるオプションがあります。

    これはdependenciesを置きます。これはこのプロパティは: [これらのプロパティに依存する]というような感じで置きます。

    例えば以下はbarを置くなら必ずfooを定義しなければならないというスキーマの例です。

    {
      "type": "object",
      "properties": {
        "foo": {
          "type": "string"
        },
        "bar": {
          "type": "number"
        },
      },
      "dependencies": {
        "bar": ["foo"]
      }
    }

    プロパティ名のパターン

    propertyNames: {pattern:<pattern>}を置くとプロパティ名はそのパターンにマッチするものだけ期待するという意味にできます。以下はcolor.orangecolor.blueなどcolor.からはじまるプロパティだけを期待する例です。

    {
      "type": "object",
      "propertyNames": {
       "pattern": "^color\\."
      }
    }

    プロパティの数

    minProperties:<number>maxProperties:<number>を置くことでそれぞれ、「これ以上はプロパティ数を置いてほしい」、「これ以下にプロパティ数を抑えてほしい」という意味にできます。

    {
      "type": "object",
      "minProperties": 2,
      "maxProperties": 3
    }

    配列の設定

    type:arrayとすることで何らかの構造の配列を期待するという意味になります。そして、何らかのと書いたように中身をある程度絞り込むようなオプションがいくつかあります。

    構成アイテムの型を設定

    items:<定義>プロパティで設定できます。例えば以下のような書き方で「数値の配列」という意味にできます。

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

    いくつかの型が混じっている

    例えばnumberfalseが混ざったものを期待したい場合はanyOfというものを使います。これは「どれかに当てはまってればいいよ」というような構造にすることができるオプションです。

    {
      "type": "array",
      "items": {
        "anyOf": [
          {
            "type": "number",
          },
          {
            "type": "boolean",
            "enum": [false]
          }
        ]
      }
    }

    タプル

    itemsの中を配列にすることでタプルにできます。

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

    これは以下のようなものを期待します。

    [
      "foo",
      123,
      false,
      null
    ]

    見て分かる通り先頭が期待した通りならあとは何でもいいという感じになってます。もし、期待した通りのタプルにしたいならadditionalItems:falseを一緒に置きます。

    {
      "type": "array",
      "items": [
        {
          "type": "string"
        },
        {
          "type": "number"
        }
      ],
      "additionalItems": false
    }
    [
      "foo",
      123
    ]

    ちなみにadditionalItemsに文字列などを設定すると、定義以降の部分の型をそれに絞ることができます。

    {
      "type": "array",
      "items": [
        {
          "type": "string"
        },
        {
          "type": "number"
        }
      ],
      "additionalItems": {
        "type": "string"
      }
    }
    [
      "foo",
      123,
      "bar",
      "baz"
    ]

    ユニークな値

    重複した値は欲しくない時があるかもしれません。そんな時はuniquness:trueを共に置きます。

    {
      "type": "array",
      "uniquness": true
    }

    これで以下のような定義は駄目になります。

    [
      "foo",
      "foo"
    ]

    配列のサイズ

    minItemsmaxItemsを使います。それぞれ、最小の配列サイズと最大の配列サイズを設定できます。

    {
      "type": "array",
      "minItems": 2,
      "maxItems": 3
    }

    複雑な型の設定

    複雑な型というのは

    • 複数の条件すべてにマッチしなければならない
    • どれかマッチしなければならない(ホワイトリスト)
    • 複数の条件のうち1つにマッチしなければならない
    • マッチしてはならない(ブラックリスト)

    などがあります。

    複数の条件すべてにマッチしなければならない

    これにはallOfを使います。

    {
      "allOf": [
        {
          "type": "string"
        },
        {
          "pattern": "\w+"
        }
      ]
    }

    別れているので少しややこしいですが、以下のようにするのと同じ意味です。

    {
      "type": "string",
      "pattern": "\w+"
    }

    主に#refな条件を組み合わせる為に使ったりします。

    どれかマッチしなければならない(ホワイトリスト)

    これにはanyOfを使います。「ここではstringnumberを置いてほしい」という要望なら、以下のようになります。

    {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "number"
        }
      ]
    }

    複数の条件のうち1つにマッチしなければならない

    これにはoneOfを使います。例えば以下のようなスキーマだったとします。

    {
      "oneOf": [
        {
          "type": "string"
          "pattern": "^a"
        },
        {
          "type": "string"
          "pattern": "z$"
        }
      ]
    }

    ここにはaazzは置くことができますが、azは両方にマッチしてしまうので置くことができません。

    マッチしてはならない(ブラックリスト)

    これにはnotを使います。例えば以下のようにすると「booleanobjectarray, null以外を期待する(stringnumber)」という意味になります。

    {
      "not": [
        {
          "type": "boolean"
        },
        {
          "type": "object"
        },
        {
          "type": "array"
        },
        {
          "type": "null"
        },
      ]
    }