(この機能はv1.1.0で公開されました)
JSON Schemaは、JSONデータの形式を記述するための標準の草案です。スキーマ自体もJSONデータです。JSON SchemaでJSON構造を検証すると、型やキーの存在などを手作業で確認せずに、コードからDOMへ安全にアクセスできます。また、シリアライズしたJSONが、指定したスキーマに適合することも保証できます。
RapidJSONには、JSON Schema Draft v4のバリデーターが実装されています。JSON Schemaに詳しくない場合は、Understanding JSON Schemaを参照してください。
基本的な使い方
まず、JSON SchemaをDocumentへ解析し、そのDocumentをSchemaDocumentへコンパイルします。
次に、そのSchemaDocumentを使ってSchemaValidatorを構築します。SAXイベントを処理するという点で、Writerと似ています。したがって、document.Accept(validator)でドキュメントを検証し、その妥当性を確認できます。
補足:
- 一つのSchemaDocmentを、複数のSchemaValidatorが参照できます。SchemaValidatorは、それを変更しません。
- SchemaValidatorは、複数のドキュメントの検証に再利用できます。別のドキュメントで使う前に、validator.Reset()を呼びます。
解析・シリアライズ中の検証
多くのJSON Schemaバリデーター実装と異なり、RapidJSONはSAXに基づくスキーマバリデーターを提供します。そのため、ストリームからJSONを解析しながら、その場で検証できます。指定したスキーマに違反するJSONの値に遭遇すると、解析は直ちに終了します。この設計は、大きなJSONファイルの解析に特に役立ちます。
DOMによる解析
DOMを使って解析する場合、Documentには、SAXイベントの受信に加え、準備と終了処理が必要です。そのため、reader・バリデーター・ドキュメントの間でイベントを受け渡すための処理が必要になります。SchemaValidatingReaderは、この処理を行うヘルパークラスです。
SAXによる解析
SAXを使った解析は、はるかに単純です。JSONを検証するだけで、その後の処理が不要なら、次のように書けます。
これは、schemavalidatorの例で使われている方法そのものです。明確な利点は、JSONの大きさにかかわらずメモリー使用量が少ないことです(メモリー使用量はスキーマの複雑さに依存します)。
さらにSAXイベントを処理する必要がある場合は、テンプレートクラスGenericSchemaValidatorを使い、バリデーターの出力ハンドラーを設定します。
シリアライズ
シリアライズ中に検証することもできます。これにより、出力JSONがJSON Schemaに適合することを保証できます。
もちろん、アプリケーションにSAX形式のシリアライズだけが必要なら、Writerの代わりにSchemaValidatorへSAXイベントを送るだけで済みます。
リモートスキーマ
JSON Schemaは、ローカルまたはリモートのスキーマを参照するJSON Pointerである、$ref キーワードをサポートします。ローカルのポインターは#で始まり、リモートのポインターは相対または絶対URIです。例を示します。
SchemaDocumentには、このようなURIを解決する方法が分からないため、利用者が提供するIRemoteSchemaDocumentProviderのインスタンスが必要です。
適合性
RapidJSONは、JSON Schema Test Suite(JSON Schema draft 4)の263テスト中262テストに合格しました。
失敗したのは、refRemote.jsonの「change resolution scope」にある「changed scope ref invalid」テストです。スキーマのキーワードidと、URIを結合する機能が実装されていないためです。
また、文字列の値に対するスキーマのキーワードformatは、仕様で必須とされていないため無視されます。
正規表現
スキーマのキーワードpatternとpatternPropertiesは、正規表現で要求するパターンを照合します。
RapidJSONには単純なNFA正規表現エンジンが実装されており、既定で使われます。次の構文に対応しています。
| 構文 | 説明 |
|---|---|
| ab | 連結 |
| a|b | 選択 |
| a? | 0回または1回 |
| a* | 0回以上 |
| a+ | 1回以上 |
| a{3} | ちょうど3回 |
| a{3,} | 3回以上 |
| a{3,5} | 3回から5回 |
| (ab) | グループ化 |
| ^a | 先頭 |
| a$ | 末尾 |
| . | 任意の文字 |
| [abc] | 文字クラス |
| [a-c] | 文字クラスの範囲 |
| [a-z0-9_] | 文字クラスの組み合わせ |
| [^abc] | 否定文字クラス |
| [^a-c] | 否定文字クラスの範囲 |
| [\b] | バックスペース(U+0008) |
| \|, \\, ... | 文字のエスケープ |
| \f | 改ページ(U+000C) |
| \n | 改行(U+000A) |
| \r | 復帰(U+000D) |
| \t | タブ(U+0009) |
| \v | 垂直タブ(U+000B) |
C++11コンパイラーでは、RAPIDJSON_SCHEMA_USE_INTERNALREGEX=0とRAPIDJSON_SCHEMA_USE_STDREGEX=1を定義することで、std::regexも使えます。スキーマでpatternとpatternPropertiesが不要なら、両方のマクロを0にしてこの機能を無効にすると、コードサイズをいくらか小さくできます。
性能
C++のJSONライブラリーの多くは、まだJSON Schemaに対応していません。そこで、Node.js上で動く11のJavaScriptライブラリーをテストするjson-schema-benchmarkに合わせて、RapidJSONのJSON Schemaバリデーターの性能を評価しました。
このベンチマークは、JSON Schema Test Suiteを使って検証を実行しますが、一部のテストスイートとテストを除外しています。schematest.cppに、同じベンチマーク手順を実装しました。
Mac Book Pro(2.8 GHz Intel Core i7)で、次の結果が得られました。
| バリデーター | 相対速度 | 1秒当たりのテスト実行回数 |
|---|---|---|
| RapidJSON | 155% | 30682 |
| ajv | 100% | 19770 (± 1.31%) |
| is-my-json-valid | 70% | 13835 (± 2.84%) |
| jsen | 57.7% | 11411 (± 1.27%) |
| schemasaurus | 26% | 5145 (± 1.62%) |
| themis | 19.9% | 3935 (± 2.69%) |
| z-schema | 7% | 1388 (± 0.84%) |
| jsck | 3.1% | 606 (± 2.84%) |
| jsonschema | 0.9% | 185 (± 1.01%) |
| skeemas | 0.8% | 154 (± 0.79%) |
| tv4 | 0.5% | 93 (± 0.94%) |
| jayschema | 0.1% | 21 (± 1.14%) |
つまり、RapidJSONは最も速いJavaScriptライブラリー(ajv)の約1.5倍、最も遅いものの1400倍の速さです。