我一直在努力尝试根据元模式验证 json 模式(检查 json 是否确实遵循 JSON 模式标准)。 我尝试遵循文档link,link。我基于官方 JSON Schema 规范
我的用例是这样的:我正在开发一个端点,它可以接收带有模式的 json。该模式稍后将用于验证某些实体,但我也想验证模式本身。
我尝试了所有这些,但它们都返回相同的结果..有效..所以在我看来他们没有验证任何内容..
private void ValidateSchema(string schemaString)
{
var element = JsonNode.Parse(schemaString);
var metaSchema = Json.Schema.MetaSchemas.Metadata202012; // tried also with Json.Schema.MetaSchemas.Draft202012;
var options = new ValidationOptions
{
OutputFormat = OutputFormat.Detailed,
ValidateMetaSchema = false // tried also with true
};
var results = metaSchema.Validate(element, options);
}
这些是我尝试过的输入。我预计有些会返回无效。
@"{""f"":""a""}"
@"{}"
@"{""required"": [""prop1"", ""prop2"", ""prop3"", ""prop4"", ""prop5"", ""prop6""]}"
@"{
""$schema"": ""http://json-schema.org/draft-07/schema#"",
""type"": ""object"",
""required"": [""prop1"", ""prop2"", ""prop3"", ""prop4"", ""prop5"", ""prop6""]
}"
就代码而言,您做得正确,并且不需要
ValidateMetaSchema
选项。该选项根据其元模式验证模式,同时您根据模式验证其他 JSON 数据。
例如,如果我反序列化了你的最后一个示例
{
"$schema": ""http://json-schema.org/draft-07/schema#",
"type": "object",
"required": [
"prop1",
"prop2",
"prop3",
"prop4",
"prop5",
"prop6"
]
}
进入
JsonSchema
,我使用它来验证其他一些 JSON 数据,然后该选项将根据草案 7 元架构添加架构的二次验证。如果架构由于某种原因无效(例如,在草案 7 中,"$defs": 42
将被忽略,但在草案 2020-12 中它无效),则 $schema
关键字将引发错误,该错误将包含在输出中。
在您的情况下,您将直接根据其元模式(只是其本身)验证草案 7 元模式。但我们已经知道草案 7 元模式对其自身有效,因此这只是一个不必要的额外检查。继续并关闭该选项。
在评论中,您询问是否有办法在存在未知关键字时引发错误。没有这样的选项。
但是,您可以做的是检查
schema.Keywords
属性是否有任何 UnrecognizedKeyword
类型。如果有的话,那么架构中有额外的数据。
但请注意模式可以嵌套,因此您需要检查每个级别。
{
"allOf": [
{ "unrecognized": "keyword" }
]
}
在这里,您需要找到
AllOfKeyword
并检查其子模式中的 UnrecognizedKeyword
。
除此之外,我将稍微扩展@Clemens 的答案,以解释为什么你的示例返回有效。
{
"f": "a"
}
当针对元模式进行验证时,此 JSON 将产生与第二个示例相同的验证结果
{}
因为(如 @Clemens 提到的)JSON 模式会忽略未知关键字。由于 f
不是可识别的关键字,因此验证会忽略它。 (不过,为 f
输出收集了注释。)
因为其中没有验证关键字,所以它将验证所有 JSON 实例。 技术上这是一个有效的模式,尽管它没有做太多事情。
{
"required": [
"prop1",
"prop2",
"prop3",
"prop4",
"prop5",
"prop6"
]
}
在这里,您需要存在某些属性 if JSON 实例是一个对象。但如果实例 不是 对象,则
required
不起作用。您可能希望使用 "type": "object"
来更多地限制值。
{
"$schema": ""http://json-schema.org/draft-07/schema#",
"type": "object",
"required": [
"prop1",
"prop2",
"prop3",
"prop4",
"prop5",
"prop6"
]
}
在这里,所有的部分都已就位,并且您可能期望的可以发挥作用。 JSON 仍然是有效的草案 7 架构(它对于草案 2020-12 也有效)。
为了使架构无效,您必须为其不支持的已定义关键字添加有效值,例如为
maximum
提供一个字符串值。在这种情况下,架构将无法验证。
也就是说,如果您尝试将无效模式 JSON 反序列化为
JsonSchema
模型,序列化程序将抛出异常,因为反序列化期间会发生 some 验证。
我认为根据元模式验证模式 JSON 的方法比让序列化程序抛出异常更好,但您需要确保根据
$schema
关键字中表示的元模式进行验证。 (因此,不要根据草案 7 元模式验证 2020-12 草案模式。)
元模式验证未涵盖您的示例。元模式使用开放模型,并且也没有语义检查。您需要一个 JSON 模式 linter,例如 JSONBuddy (https://www.json-buddy.com) 附带的那种,也可以在 json-schema-linter.com 上获取以进行快速测试。
我在这里发布了我在 @gregsdennis 的回答后使用的代码,这样任何人都可以使用它:
private static void ValidateSchema(string schema)
{
JsonSchema jsonSchema;
try
{
jsonSchema = JsonSerializer.Deserialize<JsonSchema>(schema)!;
}
catch (Exception e)
{
throw new ArgumentException($"Submitted schema is invalid. Could not deserialize. Error: {e.Message}");
}
if (jsonSchema.Keywords == null)
{
throw new ArgumentException($"Submitted schema is invalid. No Keywords found.");
}
var unrecognizedKeywords = jsonSchema.Keywords!.Where(k => k is UnrecognizedKeyword).ToArray();
if (unrecognizedKeywords.Any())
{
var data = string.Join(", ", unrecognizedKeywords.Select(SerializeForLogging));
throw new ArgumentException($"Submitted schema is invalid. Unrecognized Keywords: {data}");
}
}