次のような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 property instance path: /foo
jsonschemaでは内部スキーマを指す$ref
を適切に解決するResolver
を持っているので、JSONSchema::compile
でスキーマをコンパイルするときに$ref
も自動的に解決される。
- serde_json 1.0.108, jsonschema 0.17.1を利用した↩