由于“type”是 JSON 架构关键字,为了清楚起见,我将按照您的指导并使用“t”作为类型区分字段。
没有特定的关键字来完成或指示这一点(但是,请参阅https://github.com/json-schema-org/json-schema-spec/issues/31 https://github.com/json-schema-org/json-schema-spec/issues/31供讨论)。这是因为,为了验证的目的,您需要做的一切都已经是可能的。在 JSON 模式中,错误是次要的验证。我们要做的就是限制我们看到的错误数量,因为很明显,到了某个点,错误就不再有效。
通常,当您验证消息时,您首先知道其类型,然后阅读消息的其余部分。例如,在 HTTP 中,如果您正在阅读以以下开头的行Date:
并且下一个字符不是数字或字母,您可以立即发出错误(例如“意外的波浪号,预期月份名称”)。
但在 JSON 中,情况并非如此,因为属性是无序的,并且您可能直到最后才遇到“t”(如果有的话)。 “如果/那么”可以帮助解决这个问题。
但首先,首先要找出最重要的限制因素,并将它们移到顶部。
首先,使用"type": "object"
and "required":["t"]
在您的顶级架构中,因为在所有情况下都是如此。
其次,使用“properties”和“enum”枚举其所有有效值。这样,如果“t”确实输入错误,它将是顶级架构中的错误,而不是子架构中的错误。
如果所有这些约束都通过,但文档仍然无效,那么更容易断定问题一定是消息的其他内容,而不是“t”属性本身。
现在在每个子模式中,使用"const"
将子模式与类型名称相匹配。
我们得到这样的模式:
{
"type": "object",
"required": ["t"],
"properties": { "t": { "enum": ["message_type_1", "message_type_2"] } }
"oneOf": [
{
"type": "object",
"properties": {
"t": { "const": "message_type_1" }
}
},
{
"type": "object",
"properties":
"t": { "const": "message_type_2" },
"some_other_property": {
"type": "integer"
}
},
"required": [ "some_other_property" ]
}
]
}
现在,将每种类型拆分为不同的架构文件。通过以“t”命名该文件,使其可供人类访问。这样,应用程序可以读取对象流并选择模式来验证每个对象。
{
"type": "object",
"required": ["t"],
"properties": { "t": { "enum": ["message_type_1", "message_type_2"] } }
"oneOf": [
{"$ref": "message_type_1.json"},
{"$ref": "message_type_2.json"}
]
}
理论上,验证器现在拥有足够的信息来产生更清晰的错误(尽管我不知道有任何验证器可以做到这一点)。
因此,如果这不能为您生成足够干净的错误报告,您有两个选择:
首先,您可以自己实现部分验证过程。如上所述,使用流式 JSON 解析器,例如Oboe.js http://www.oboejs.com/要读取流中的每个对象,解析该对象并读取“t”属性,然后应用适当的架构。
或者其次,如果您确实想纯粹在 JSON Schema 中执行此操作,请在“allOf”内使用“if/then”语句:
{
"type": "object",
"required": ["t"],
"properties": { "t": { "enum": ["message_type_1", "message_type_2"] } }
"allOf": [
{"if":{"properties":{"t":{"const":"message_type_1"}}}, "then":{"$ref": "message_type_1.json"}},
{"if":{"properties":{"t":{"const":"message_type_2"}}}, "then":{"$ref": "message_type_2.json"}}
]
}
这应该会产生以下错误:
t
不是“message_type_1”或“message_type_2”之一
or
(因为t
=“消息_类型_2”)some_other_property
不是整数
但两者都不是。