如何根据字段描述的对象类型针对JSON模式验证JSON对象? [英] How to validate a JSON object against a JSON schema based on object's type described by a field?

查看:65
本文介绍了如何根据字段描述的对象类型针对JSON模式验证JSON对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一些需要针对JSON模式(草案04)进行验证的对象(消息).每个对象都保证有一个类型"字段来描述其类型,但是每种类型都有一组完全不同的其他字段,因此每种类型的对象都需要一个唯一的架构.

我看到了几种可能性,没有哪一种特别吸引人,但我希望我错过了一些东西.

可能性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:嵌套"if","then","else"三合会.我还没有尝试过,但是我猜想在这种情况下错误可能会更好.但是,编写起来非常麻烦,因为如果堆积起来就嵌套了.

可能性3:针对每个"t"的可能值的单独方案.这是最简单的解决方案,但是我不喜欢它,因为它使我无法在架构中使用公共元素(通过引用).

那么,这些是我唯一的选择,还是我可以做得更好?

解决方案

由于类型"是JSON模式关键字,为了清楚起见,我将按照您的指导,将"t"用作类型区分字段. >

没有特定的关键字可以完成或表明这一点(但是,请参见

但是在JSON中,这是不正确的,因为属性是无序的,并且直到最后都可能遇到"t"(如果有的话). 如果/那么"可以帮助解决这个问题.

但是首先,首先要排除最重要的约束,然后将它们移到顶部.

首先,在顶级架构中使用"type": "object""required":["t"],因为在所有情况下都是如此.

第二,使用属性"和枚举"枚举其所有有效值.这样,如果确实输入了"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 )读取流中的每个对象,对象并读取"t"属性,然后应用适当的架构.

或者第二,如果您真的想纯粹在JSON模式中执行此操作,请在"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不是整数

而不是两者兼而有之.

I have a number of objects (messages) that I need to validate against a JSON schema (draft-04). Each objects is guaranteed to have a "type" field, which describes its type, but every type have a completely different set of other fields, so each type of object needs a unique schema.

I see several possibilities, none of which are particularly appealing, but I hope I'm missing something.

Possibility 1: Use oneOf for each message type. I guess this would work, but the problem is very long validation errors in case something goes wrong: validators tend to report every schema that failed, which include ALL elements in "oneOf" array.

{
  "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"
      ]
    }
  ]
}

Possibility 2: Nested "if", "then", "else" triads. I haven't tried it, but I guess that maybe errors would be better in this case. However, it's very cumbersome to write, as nested if's pile up.

Possibility 3: A separate scheme for every possible value of "t". This is the simplest solution, however I dislike it, because it precludes me from using common elements in schemas (via references).

So, are these my only options, or can I do better?

解决方案

Since "type" is a JSON Schema keyword, I'll follow your lead and use "t" as the type-discrimination field, for clarity.

There's no particular keyword to accomplish or indicate this (however, see https://github.com/json-schema-org/json-schema-spec/issues/31 for discussion). This is because, for the purposes of validation, everything you need to do is already possible. Errors are secondary to validation in JSON Schema. All we're trying to do is limit how many errors we see, since it's obvious there's a point where errors are no longer productive.

Normally when you're validating a message, you know its type first, then you read the rest of the message. For example in HTTP, if you're reading a line that starts with Date: and the next character isn't a number or letter, you can emit an error right away (e.g. "Unexpected tilde, expected a month name").

However in JSON, this isn't true, since properties are unordered, and you might not encounter the "t" until the very end, if at all. "if/then" can help with this.

But first, begin by by factoring out the most important constraints, and moving them to the top.

First, use "type": "object" and "required":["t"] in your top level schema, since that's true in all cases.

Second, use "properties" and "enum" to enumerate all its valid values. This way if "t" really is entered wrong, it will be an error out of your top-level schema, instead of a subschema.

If all of these constraints pass, but the document is still invalid, then it's easier to conclude the problem must be with the other contents of the message, and not the "t" property itself.

Now in each sub-schema, use "const" to match the subschema to the type-name.

We get a schema like this:

{
  "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" ]
     }
  ]
}

Now, split out each type into a different schema file. Make it human-accessible by naming the file after the "t". This way, an application can read a stream of objects and pick the schema to validate each object against.

{
  "type": "object",
  "required": ["t"],
  "properties": { "t": { "enum": ["message_type_1", "message_type_2"] } }
  "oneOf": [
     {"$ref": "message_type_1.json"},
     {"$ref": "message_type_2.json"}
  ]
}

Theoretically, a validator now has enough information to produce much cleaner errors (though I'm not aware of any validators that can do this).

So, if this doesn't produce clean enough error reporting for you, you have two options:

First, you can implement part of the validation process yourself. As described above, use a streaming JSON parser like Oboe.js to read each object in a stream, parse the object and read the "t" property, then apply the appropriate schema.

Or second, if you really want to do this purely in JSON Schema, use "if/then" statements inside "allOf":

{
  "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"}}
  ]
}

This should produce errors to the effect of:

t not one of "message_type_1" or "message_type_2"

or

(because t="message_type_2") some_other_property not an integer

and not both.

这篇关于如何根据字段描述的对象类型针对JSON模式验证JSON对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆