我需要根据JSON模式(draft-04)验证许多对象(消息)。每个对象都保证有"类型" field,描述其类型,但每种类型都有一组完全不同的其他字段,因此每种类型的对象都需要一个唯一的模式。
我看到了几种可能性,其中没有一种特别吸引人,但我希望我能错过一些东西。
可能性1:对每种消息类型使用oneOf。我想这会起作用,但问题是很长的验证错误以防出现问题:验证器倾向于报告每个失败的模式,其中包括" oneOf"阵列。
{
"oneOf":
[
{
"type": "object",
"properties":
{
"t":
{
"type": "string",
"enum":
[
"message_type_1"
]
}
}
},
{
"type": "object",
"properties":
{
"t":
{
"type": "string",
"enum":
[
"message_type_2"
]
},
"some_other_property":
{
"type": "integer"
}
},
"required":
[
"some_other_property"
]
}
]
}
可能性2:嵌套"如果","那么","否则"黑社会。我还没有尝试过,但我想在这种情况下错误可能更好。但是,写起来非常麻烦,因为嵌套会堆积起来。
可能性3:针对" t"的每个可能值的单独方案。这是最简单的解决方案,但我不喜欢它,因为它阻止我在模式中使用公共元素(通过引用)。
那么,这些是我唯一的选择,还是我可以做得更好?
答案 0 :(得分:2)
由于“type”是一个JSON Schema关键字,为了清楚起见,我将按照您的主题并使用“t”作为类型区分字段。
没有特定的关键字可以完成或表明这一点(但是,请参阅https://github.com/json-schema-org/json-schema-spec/issues/31进行讨论)。这是因为,为了验证,您需要做的一切都已成为可能。错误是JSON Schema中验证的次要因素。我们所要做的只是限制我们看到的错误数量,因为很明显错误不再有效。
通常,当您验证消息时,首先知道其类型,然后阅读消息的其余部分。例如,在HTTP中,如果您正在读取以Date:
开头并且下一个字符不是数字或字母的行,则可以立即发出错误(例如“意外的代字号,预期的月份名称” )。
然而,在JSON中,这不是真的,因为属性是无序的,并且如果有的话,你可能不会遇到“t”直到最后。 “if / then”可以帮助解决这个问题。
但首先,首先要考虑最重要的约束,并将它们移到顶部。
首先,在您的顶级架构中使用"type": "object"
和"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"}
]
}
理论上,验证器现在有足够的信息来产生更清晰的错误(尽管我不知道有任何验证器可以做到这一点)。
因此,如果这不能为您提供足够干净的错误报告,您有两种选择:
首先,您可以自己实施部分验证过程。如上所述,使用像Oboe.js这样的流式JSON解析器来读取流中的每个对象,解析对象并读取“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”
或
(因为
t
=“message_type_2”)some_other_property
不是整数
而不是两者。