根据包含多个模式定义的JSON模式进行验证

3

我正在为在Postman中使用的JSON模式创建一个包含多个架构定义的JSON模式。我需要根据该模式验证的JSON与其中一个架构定义匹配。

示例模式

{
  "oneOf": [
    {
      "$ref": "#/definitions/schema1"
    },
    {
      "$ref": "#/definitions/schema2"
    },
    {
      "$ref": "#/definitions/schema3"
    }
  ],
  "definitions": {
    "schema1": {
      "type": "object",
      "properties": {
        "propertyA": {
          "type": "string"
        }
      },
      "required": [
        "propertyA"
      ]
    },
    "schema2": {
      "type": "object",
      "properties": {
        "propertyB": {
          "type": "string"
        }
      },
      "required": [
        "propertyB"
      ]
    },
    "schema3": {
      "type": "object",
      "properties": {
        "propertyC": {
          "type": "string"
        }
      },
      "required": [
        "propertyC"
      ]
    }
  }
}

示例JSON数据

该JSON数据将根据模式进行验证,并正确标记为无效(因为需要字符串):

{
    "propertyA": 123
}

问题

根据https://www.jsonschemavalidator.net/,此示例返回4个错误:

  • 消息:JSON与“oneOf”中没有模式匹配。 模式路径:#/oneOf
  • 消息:无效类型。 期望的是字符串,但得到的是整数。 模式路径:#/definitions/schema1/properties/propertyA/type
  • 消息:对象缺少必需的属性:propertyC。
    模式路径:#/definitions/schema3/required
  • 消息:对象缺少必需的属性:propertyB。
    模式路径:#/definitions/schema2/required

我只对期望一个字符串的错误消息感兴趣。 在保持模式定义在1个单独文件中的同时,如何避免其他错误消息?


想象一下你有一个模式,类似于oneof string,number,但是去掉了null。你希望验证抛出哪个错误?这是相同的逻辑,验证不知道你感兴趣的是哪一个,因为所有内容都是无效的。 - PDHide
1
@PDHide,我从您的评论中了解到验证程序不知道要验证哪个架构,因此会针对所有架构进行验证。因此,我想真正的问题是如何避免这种情况。Jason Desrosiers的回答似乎可以解决这个问题。 - PixelPlex
是的,这确实是一个很好的答案。 - PDHide
1个回答

5

没错,oneOf 对于这种情况确实不太好用。使用 if/then 虽然更冗长,但可以获得更好的结果。基本上,您需要确定某些条件来确定模式是否适用于实例。

通常,该条件是一个常见字段的值。在这种情况下,如果要验证的实例是具有 "type": "A" 的对象,则必须针对 /definitions/a 模式进行验证。如果它具有 "type": "B",则必须针对 /definitions/b 模式进行验证。

{
  "allOf": [
    {
      "if": {
        "properties": {
          "type": { "const": "A" }
        },
        "required": ["type"]
      },
      "then": { "$ref": "#/definitions/a" }
    },
    {
      "if": {
        "properties": {
          "type": { "const": "B" }
        },
        "required": ["type"]
      },
      "then": { "$ref": "#/definitions/b" }
    }
  ]
}

如果您的条件是某个特定字段的存在,那么您可以使用 dependencies 关键字作为快捷方式。如果要验证的实例是一个带有 "propertyA" 属性的对象,则该实例必须与 /definitions/a 模式匹配才能有效。对于 "propertyB" 亦然。
{
  "dependencies": {
    "propertyA": { "$ref": "#/definitions/a" },
    "propertyB": { "$ref": "#/definitions/b" }
  }
}

其实你提供的例子有一个超级简单的解决方案,但我回答通用情况是因为我假设你的实际模式比这个例子更复杂。
{
  "type": "object",
  "properties": {
    "propertyA": { "type": "string" },
    "propertyB": { "type": "string" },
    "propertyC": { "type": "string" }
  },
  "oneOf": [
    { "required": ["propertyA"] },
    { "required": ["propertyB"] },
    { "required": ["propertyC"] }
  ]
}

很棒的回答,Jason :-). 你猜对了,我的真实模式比较复杂。我可能把例子简化得有点过了。在确定需要验证的模式之前,我需要识别多个字段。我认为if/then关键字确实是我需要的。我会尝试一下,如果可以的话,我会标记你的答案。 - PixelPlex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接