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