クラスからスキーマを生成し、AllowAdditionalProperties = true を設定したにも関わらずJSONに定義外のプロパティを記載するとバリデーションエラーが発生してしまいました。
調べても中々良いのが見つからなかったのでメモしておきます。
環境
- .NET 7
- NJsonSchema 10.8.0
上手くいっていなかったコード
最初は以下のようなコードでスキーマを作成しバリデーションを行っていました。
// スキーマ作成
var njSchema = NJsonSchema.JsonSchema.FromType();
njSchema.AllowAdditionalProperties = true;
// スキーマをファイル出力
File.WriteAllText("njsonschema.json", njSchema.ToJson());
// バリデーション
var jsonText = File.ReadAllText("sample.json");
var result = njSchema.Validate(jsonText);
sample.json
{
"id1": "Id1",
"undefined1": "余計な項目",
"nested1": {
"id2": "Id2",
"undefined2": "余計な項目",
"nested2": {
"id3": "Id3",
"undefined3": "余計な項目"
}
}
}
TestModel
using Newtonsoft.Json;
namespace NJsonSchemaSample
{
internal class TestModel
{
[JsonProperty("id1", Required = Required.Always)]
public string Id1 { get; set; }
[JsonProperty("nested1")]
public NestedModel Nested1 { get; set; }
public class NestedModel
{
[JsonProperty("id2")]
public string Id2 { get; set; }
[JsonProperty("nested2")]
public NestedModel Nested2 { get; set; }
public class Nested2Model
{
[JsonProperty("id3")]
public string Id3 { get; set; }
}
}
}
}
njSchema.ToJson()
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "TestModel",
"type": "object",
"required": [
"id1"
],
"properties": {
"id1": {
"type": "string"
},
"nested1": {
"$ref": "#/definitions/NestedModel"
}
},
"definitions": {
"NestedModel": {
"type": "object",
"additionalProperties": false,
"properties": {
"id2": {
"type": "string"
},
"nested2": {
"$ref": "#/definitions/NestedModel"
}
}
}
}
}
しかし、これだと一番上の階層のみAdditionalProperties = trueとなり、ネストした階層ではAdditionalProperties = falseのままとなっていました。
(“additionalProperties”: trueは省略される)
つまり、sample.jsonの場合だとundefined1は問題ないがundefined2、undefined3でエラーとなります。
対処方法
見つけた方法は2つ。
対処方法1
https://stackoverflow.com/questions/38803798/how-can-i-set-the-allowadditionalproperties-to-true-in-generating-the-schema-wit
こちらはこのままコピーしてもビルドが通りません。かなり前の質問なのでバージョンの差異だと思います。
最初はビルド通らないから諦めて別の方法ないか探していましたがやってみたら出来ました。
MyJsonSchemaGenerator.cs
using NJsonSchema;
using NJsonSchema.Generation;
namespace NJsonSchemaSample
{
internal class MyJsonSchemaGenerator : JsonSchemaGenerator
{
public MyJsonSchemaGenerator(JsonSchemaGeneratorSettings settings)
: base(settings)
{
}
protected override void GenerateObject(JsonSchema schema, JsonTypeDescription typeDescription, JsonSchemaResolver schemaResolver)
{
base.GenerateObject(schema, typeDescription, schemaResolver);
schema.AllowAdditionalProperties = true;
}
}
}
使用方法
var generator = new MyJsonSchemaGenerator(new JsonSchemaGeneratorSettings());
var njSchema = generator.Generate(typeof(TestModel));
出来上がったスキーマがこちらです。
“additionalProperties”: false がなくなっています。
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "TestModel",
"type": "object",
"required": [
"id1"
],
"properties": {
"id1": {
"type": "string"
},
"nested1": {
"$ref": "#/definitions/NestedModel"
}
},
"definitions": {
"NestedModel": {
"type": "object",
"properties": {
"id2": {
"type": "string"
},
"nested2": {
"$ref": "#/definitions/NestedModel"
}
}
}
}
}
対処方法2
2つ目はNJsonSchema.JsonSchema.Definitionsを使用する方法です。スキーマを見るとネストしている場合は$refでdefinitionsを参照するようになっています。
なのでdefinitionsにadditionalProperties = trueを設定してあげます。
以下のソースで言うとforeachの部分です。こちらで作成したスキーマと方法1で作成したスキーマは全く同じスキーマとなります。
一点注意がいるのが njSchema.AllowAdditionalProperties = true; が必要ってところです。definitionsをforeachで回せばOKだろうと思っていましたが、一番上の階層には反映されないので消さずに残しておきましょう。
// スキーマ作成
var njSchema = NJsonSchema.JsonSchema.FromType();
njSchema.AllowAdditionalProperties = true;
foreach (var def in njSchema.Definitions)
{
def.Value.AllowAdditionalProperties = true;
}
// スキーマをファイル出力
File.WriteAllText("njsonschema.json", njSchema.ToJson());
// バリデーション
var jsonText = File.ReadAllText("sample.json");
var result = njSchema.Validate(jsonText);
コメント