JSON SchemaでJSONのValidationをする

JSONのValidationをしたい

JSONがデータのフォーマットとして使われだして久しいですが、
現在ではREST APIのやり取り、ElasticsearchやMongoDBでの通信、設定ファイルなど
さまざまな場所で使用されています。

データ形式がシンプルで、柔軟性があるのがJSONの特徴ですが、
データの定義をしっかり決めたいときもあります。

JSONのValidationをする必要があり、
JSON SchemaやJSONのValidationを使用したのでその紹介をします。

quickType

ここでも紹介されていますが、quicktypeとは
JSON Schemaからいろいろな言語コード(Swift, Go, Rust,JavaScript等)を生成したり、
JSONデータからJSON Schemaを生成したりできるツールです。
JSON SchemaはJSONを記述したり検証したりするための設計書のようなもので、
どんなフィールドがあるとか、そのフィールドが必須なのか等がわかります。

//quicktypeで生成したJSON Schema
{
    "$schema": "http://json-schema.org/draft-06/schema#",
    "$ref": "#/definitions/Schema",
    "definitions": {
        "Schema": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
                "foo": {
                    "type": "string"
                }
            },
            "required": [
                "foo"
            ],
            "title": "Schema"
        }
    }
}

quicktypeをcliで使う

npmを使えばインストールできます。
Globalオプションでインストールしましょう。

% npm install -g quicktype

では、JSON SchemaをJSONから生成してみます。JSONデータをもとにschema.jsonが生成されます。

% echo '{"foo" : "bar"}' | quicktype -o schema.json --lang schema
% cat schema.json
{
    "$schema": "http://json-schema.org/draft-06/schema#",
    "$ref": "#/definitions/Schema",
    "definitions": {
        "Schema": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
                "foo": {
                    "type": "string"
                }
            },
            "required": [
                "foo"
            ],
            "title": "Schema"
        }
    }
}

これはコマンドラインから実行する方法ですが、今回はJavaScriptのコードから
quicktypeを実行してJSON Schemaを生成してみましょう。

環境

今回使用した動作環境は以下のとおりです。

  • OS : MacOS X 10.14.5
  • node.js : v12.9.0

quicktype-core

コード内でJSON Schemaを生成方法について調べたところ、quicktype-coreを使えば
コード内でSchemaの生成などができるようになるとのことです。
npmをつかってインストールしましょう。

% npm install quicktype-core

quicktype-coreライブラリを使って任意のJSON文字列からJSON Schemaを生成してみます。

//sample.js
const {InputData, JSONSchemaTargetLanguage,
    jsonInputForTargetLanguage, quicktypeMultiFileSync} = require("quicktype-core");

//JSON Schema生成元モデル
const json = "{\"foo\" : 10,\"bar\": \"abc\"}";

const input = new InputData();
const srcJsonData = {name: 'SampleSchema', samples: }
const makeInput = () => jsonInputForTargetLanguage(new JSONSchemaTargetLanguage(), undefined, false)
input.addSourceSync("json", srcJsonData, makeInput)


const qtResult = quicktypeMultiFileSync({
    lang: new JSONSchemaTargetLanguage(),
    inputData: input
});

//生成したスキーマ
const schema = JSON.parse(qtResult.get("stdout").lines.join(''));
console.log(schema);

sample.jsを実行してみると、JSON Schemaが取得できるようになりました。

% node sample.js

{
  '$schema': 'http://json-schema.org/draft-06/schema#',
  '$ref': '#/definitions/SampleSchema',
  definitions: {
    SampleSchema: {
      type: 'object',
      additionalProperties: false,
      properties: [Object],
      required: [Array],
      title: 'SampleSchema'
    }
  }
}

ajvつかってvalidation

JSON Schemaを取得できたら、対象のJSONがそのSchemaに対してvalidかどうかチェックします。
ajvというJSON Schema Validatorを使ってチェックしましょう。

% npm install ajv

さきほどのプログラムに、ajvを使ったvalidationを追加します。

・・・
const Ajv = require('ajv');
const ajv = new Ajv({allErrors: true});
ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json'));

const testValidJson = {"foo": 40,"bar":"def"};
var valid = ajv.validate(schema, testValidJson);
console.log(valid);

testValidJsonはJSON Schema的にvalidなので問題ありません。
では、↓のようにJSON SchemaにあっていないJSONにしてみましょう。

・・・
const Ajv = require('ajv');
const ajv = new Ajv({allErrors: true});
ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json'));

const testInvalidJson = {"foo": "aaa","buzz":[]};
valid = ajv.validate(schema, testInvalidJson);
if (!valid) console.log(ajv.errors);

JSON Schemaと合致していないので、エラーが表示されます。

% node sample.js
[
  {
    keyword: 'additionalProperties',
    dataPath: '',
    schemaPath: '#/definitions/SampleSchema/additionalProperties',
    params: { additionalProperty: 'buzz' },
    message: 'should NOT have additional properties'
  },
  {
    keyword: 'type',
    dataPath: '.foo',
    schemaPath: '#/definitions/SampleSchema/properties/foo/type',
    params: { type: 'integer' },
    message: 'should be integer'
  },
  {
    keyword: 'required',
    dataPath: '',
    schemaPath: '#/definitions/SampleSchema/required',
    params: { missingProperty: 'bar' },
    message: "should have required property 'bar'"
  }
]

型違いやフィールド有無についてエラーが表示されています。
※設定によりエラーにするかどうかの変更も可能

これでJSONのValidationチェックができるようになりました。