JSON Schemaと友達になる為の記事

こちらの記事は[Speee Advent Calendar 2016](http://qiita.com/advent-calendar/2016/speee)の17日目の記事です。

前日の記事は[@yhatt](http://qiita.com/yhatt) の[PostCSS は ポストCSS の夢を見るか？](http://yhatt.hatenablog.jp/entry/postcss)です。

今回は新卒エンジニアである私が[JSON Schema](http://json-schema.org)の入門的な記事を書かせていただきます。

##はじめに

そもそも *JSON Schema* とは何やねん！ となる方も多いと思うので簡単に解説しておくと、
[json-schema.org](http://json-schema.org/)によって開発されているJSON objectを記述したり検証したりするための設計書のようなものです。

この *JSON Schema* ですが,

- JSON Schema Core
- JSON Schema Validation
- JSON Hyper-Schema

の3つの仕様があります。
今回は JSON Schema Core と JSON Hyper-Schema を使って一つのAPIの仕様を定義していきたいと思います。

本記事ではJSON Schemaの draft v4 を使用します。

## 基本機能編(JSON Schema Core)
### STEP 1
まずは JSON Schema Core の基本的な書き方と仕様の説明をします。

下記のようなサンタさん用の配達リスト(JSON)を返すAPIがあるとします。

```json
{
  "children": [
    {
      "id": 1,
      "name": "tarou",
      "address": "東京都港区hogehoge",
      "	presents": [
        {
          "id": 101,
          "name": "2DS",
          "price": 9,998
        },
        {
          "id": 302,
          "name": "ポケモン",
          "price": 4,298
        }
      ]
    },
    {
      "id": 2,
      "name": "hanako",
      "address": "東京都渋谷区hogehoge",
      "	presents": [
        {
          "id": 534,
          "name": "ラブあみDXセット",
          "price": 2,900
        },
      ]
    }
  ]
}
```

このJSONをJSON Schema Coreにのっとって定義すると

```json
{
  "type": "object",
  "properties": {
    "children": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "description": "子供のID",
            "type": "integer"
          },
          "name": {
            "description": "子供の名前",
            "type": "string"
          },
          "address": {
            "description": "子供の住所",
            "type": "string"
          },
          "presents": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "description": "プレゼントID",
                  "type": "integer"
                },
                "name": {
                  "description": "プレゼント名",
                  "type": "string"
                },
                "price": {
                  "description": "プレゼントの価格",
                  "type": "integer"
                }
              }
            }
          }
        }
      }
    }
  }
}
```

こんな感じになります。
JSONの構造に合わせて型を定義しただけの状態です。
非常に読みづらいですが我慢してください。
ココで使われているものの細かい説明していきます。


- `type:`

読んで字のごとし、型が何かを定義する部分です。
nullの可能性がある時は `["string", "null"]` と定義します。

- `properties:`

  こいつも読んで字のごとし、プロパティを定義する部分。

- `items:`

  中身が配列になるときはこいつを使います。

- `description:`

  説明文



ここまで出てきたものは誰がどう見ても、「まぁそうだろう」という感じのものばかりです。
簡単ですね。

### STEP 2

この読みづらいJSONの中には

- childオブジェクト
- presentオブジェクト
- その２つを持ったchildrenオブジェクト

が存在しています。
なので childオブジェクト と presentオブジェクト 切り出してみようと思います。


```json
{
  "$schema": "http://json-schema.org/draft-04/schema",
  "definitions": {
    "child": {
      "definitions": {
        "id": {
          "description": "子供のID",
          "type": "integer"
        },
        "name": {
          "description": "子供の名前",
          "type": "string"
        },
        "address": {
          "description": "プロモーションコード",
          "type": "string"
        }
      }
    },
    "present": {
      "properties": {
        "id": {
          "description": "プロモーションコード",
          "type": "integer"
        },
        "name": {
          "description": "プロモーションコード",
          "type": "string"
        },
        "price": {
          "description": "プロモーションコード",
          "type": "integer"
        }
      }
    }
  },
  "type": "object",
  "properties": {
    "children": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "$ref": "#/definitions/child/definitions/id"
          },
          "name": {
            "$ref": "#/definitions/child/definitions/name"
          },
          "address": {
            "$ref": "#/definitions/child/definitions/address"
          },
          "presents": {
            "type": "array",
            "items": {
              "$ref": "#/definitions/present"
            }
          }
        }
      }
    }
  }
}
```

結構スッキリしましたね。
ここで新たに出てきたプロパティについて説明します.

- "$schema": "http://json-schema.org/draft-04/schema"

  おまじないです。これでこのJSONではjson-schema.orgのdraft-04を使用します！ という宣言です。

- `definitions`

  スキーマのテンプレートを定義しておく場所です。
  あくまでテンプレートなのでスキーマではないので注意が必要です。

- "$ref"

  `definitions`で定義したテンプレートを参照します。
  
ここで出てきた３つは必ず覚えておくといいでしょう。
たくさん使うことになります。

## API定義編(JSON Hyper-Schema)

ここではサンタさん用の配達リスト(JSON)を返すAPIのエンドポイントなどを JSON Hyper-Schema に則って定義していこうと思います。

JSON Hyper-Schemaのメインは`links`というプロパティです。こいつのためにあるものと言っても過言ではないでしょう。


```json
{
  "$schema": "http://json-schema.org/draft-04/schema",
  "definitions": {
    "child": {
      "definitions": {
        "id": {
          "description": "子供のID",
          "type": "integer"
        },
        "name": {
          "description": "子供の名前",
          "type": "string"
        },
        "address": {
          "description": "プロモーションコード",
          "type": "string"
        }
      }
    },
    "present": {
      "properties": {
        "id": {
          "description": "プロモーションコード",
          "type": "integer"
        },
        "name": {
          "description": "プロモーションコード",
          "type": "string"
        },
        "price": {
          "description": "プロモーションコード",
          "type": "integer"
        }
      }
    },
    "children":{

    }
  },
  "type": "object",
  "properties": {
    "children": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "$ref": "#/definitions/child/definitions/id"
          },
          "name": {
            "$ref": "#/definitions/child/definitions/name"
          },
          "address": {
            "$ref": "#/definitions/child/definitions/address"
          },
          "presents": {
            "type": "array",
            "items": {
              "$ref": "#/definitions/present"
            }
          }
        }
      }
    }
  },
"links": [
    {
      "href": "http://hogehoge/api/hogehoge",
      "method": "GET",
      "rel": "self"
    }
  ]
}
```

これが一番簡単な形です。

- `href:` 
  
  APIのURLを指定する箇所

- `rel:`

  スキーマが表すインスタンスと、リンク先との関係性を表すもので色々と指定できます。
  1.`self`
    インスタンス自身のURLを指定するもの。
  2.`create` 
    新しいインスタンスを作成するリンクを表します。

　などなど

- `method:`
 
 GETとかPOSTとかPATCHとか

一番簡単な状態だとpropertiesで指定されている値を返します。
他の値を返したい場合は下記のような感じになります。

```json
"links": [
    {
      http://hogehoge/api/hogehoge
      "method": "GET",
      "rel": "self",
      "targetSchema": {
        "properties": {
          "プロパティー名": {
            "$ref": "#/definitions/hogehoge"
          }
        },
        "type": [
          "object"
        ]
      }
    }
  ]
```

と `targetSchema` をつけてあげるとそのAPIで返す値を定義できます。

受け取る値の定義は

```json
"links": [
    {
      http://hogehoge/api/hogehoge
      "method": "POST",
      "rel": "create",
      "schema": {
        "properties": {
          "プロパティー名": {
            "$ref": "#/definitions/hogehoge"
          }
        },
        "type": [
          "object"
        ]
      }
    }
  ]
```

上記のように`schema`をつけてあげると定義できます。
簡単ですね。


## おしまい

今回ざっくりJSON Schemaの入門的記事を書きました。
私が紹介したプロパティはほんの一部です。他にも沢山のプロパティが定義されています。
興味が出た方はぜひ[json-schema.org](http://json-schema.org)を見ていただければ幸いです。

本記事で紹介しなかった`JSON Schema Validation`については機会があれば後日記事にしようと思います。

私自身、業務で自社カスタムしたJSON Schema定義を使用して書いている間違いがあるかもしれません。
正しく理解できておらず間違いが書いてあった場合はコメント等で指摘いただけるとうれしいです。

つたない文章に最後までお付き合い頂きありがとうございました。