Rustで$refを使うJSON Schemaを作成してバリデーションする
次のようなJSON Schemaがあるとする。
{ "type": "object", "properties": { "foo": { "$ref": "#/definitions/foo" } }, "required": [ "foo" ]}
$ref
でJSON Pointer "#/definitions/foo"
によって参照しているスキーマは内部に存在してほしいが、なぜか今は別の場所にあるとする。構造は次のような形。
{ "foo": { "properties": { "bar": { "type": "string" }, "baz": { "type": "integer" } }, "required": [ "bar", "baz" ], "type": "object" }}
このサブスキーマを本体スキーマとマージして、$ref
を通じて解決し、バリデーションに使いたい。
このようなときはserde_jsonとjsonschemaを使って1次の手順で実装できる。
serde_json::Value::as_object_mut
でJSONをマージjsonschema::JSONSchema::compile
で$ref
を解決したスキーマにコンパイル
ここで、as_object_mut
はValue
がJSON Schemaオブジェクト(プロパティを持つ型)になっている必要があるので、今は常にスキーマ本体がオブジェクト型である前提とする。
コードは次のようになる。
fn main() { // スキーマ本体 let mut schema = serde_json::json!({ "type": "object", "properties": { "foo": { "$ref": "#/definitions/foo" } }, "required": ["foo"] });
// 他の箇所から取得してきたサブスキーマ let definitions = serde_json::json!({ "foo": { "type": "object", "properties": { "bar": { "type": "string", }, "baz": { "type": "integer", } }, "required": ["bar", "baz"], }, });
// スキーマ本体にサブスキーマを追加 schema .as_object_mut() .expect("schema should be an object") .insert("definitions".to_string(), definitions);
println!( "{}", serde_json::to_string_pretty(&schema).expect("schema should be serializable") );
// スキーマのコンパイル時にJSON Pointerでサブスキーマを参照している$refを解決 let compiled = jsonschema::JSONSchema::options() .compile(&schema) .expect("schema should be valid");
// JSONのバリデーション let valid_instance = serde_json::json!({"foo": {"bar": "hello", "baz": 42}}); println!("{}のバリデーション", valid_instance); validate(&compiled, valid_instance);
let invalid_instance = serde_json::json!({"foo": {"bar": "hello"}}); println!("{}のバリデーション", invalid_instance); validate(&compiled, invalid_instance);}
fn validate(schema: &jsonschema::JSONSchema, instance: serde_json::Value) { match schema.validate(&instance) { Ok(_) => println!("instance is valid"), Err(errors) => { for error in errors { println!("validation error: {}", error); println!("instance path: {}", error.instance_path); } } };}
# 実行結果{ "definitions": { "foo": { "properties": { "bar": { "type": "string" }, "baz": { "type": "integer" } }, "required": [ "bar", "baz" ], "type": "object" } }, "properties": { "foo": { "$ref": "#/definitions/foo" } }, "required": [ "foo" ], "type": "object"}{"foo":{"bar":"hello","baz":42}}のバリデーションinstance is valid{"foo":{"bar":"hello"}}のバリデーションvalidation error: "baz" is a required propertyinstance path: /foo
jsonschemaでは内部スキーマを指す$ref
を適切に解決するResolver
を持っているので、JSONSchema::compile
でスキーマをコンパイルするときに$ref
も自動的に解決される。
脚注
-
serde_json 1.0.108, jsonschema 0.17.1を利用した ↩