首页 > 解决方案 > 使用 oneOf 的 JSON 模式数组验证问题

问题描述

希望我能在这个验证问题上找到一些帮助:我有一个 JSON 数组,它可以有多种对象类型(视频、图像)。在该数组中,对象具有rel字段值。我正在处理的情况是,只能有一个"rel": "primaryMedia"允许的对象——视频图像。

以下是图像和视频案例的对象表示,均显示"rel": "primaryMedia".

{
  "media": [
    {
      "caption": "Caption goes here",
      "id": "ncim87659842",
      "rel": "primaryMedia",
      "type": "image"
    },
    {
      "description": "Shaima Swileh arrived in San Francisco after fighting for 17 months to get a waiver from the U.S. government to be allowed into the country to visit her son.",
      "headline": "Yemeni mother arrives in U.S. to be with dying 2-year-old son",
      "id": "mmvo1402810947621",
      "rel": "primaryMedia",
      "type": "video"
    }
  ]
}

这是我创建的架构的精简版本,用于验证这一点oneOf(假设这将处理我的情况)。然而,它并没有按预期工作。

{
  "$id": "http://example.com/schema/rockcms/article.json",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "definitions": {},
  "properties": {
    "media": {
      "items": {
        "oneOf": [
          {
            "additionalProperties": false,
            "properties": {
              "caption": {
                "type": "string"
              },
              "id": {
                "type": "string"
              },
              "rel": {
                "enum": [
                  "primaryMedia"
                ],
                "type": "string"
              },
              "type": {
                "enum": [
                  "image"
                ],
                "type": "string"
              }
            },
            "required": [
              "caption",
              "id",
              "rel",
              "type"
            ],
            "type": "object"
          },
          {
            "additionalProperties": false,
            "properties": {
              "description": {
                "type": "string"
              },
              "headline": {
                "type": "string"
              },
              "id": {
                "type": "string"
              },
              "rel": {
                "enum": [
                  "primaryMedia"
                ],
                "type": "string"
              },
              "type": {
                "enum": [
                  "video"
                ],
                "type": "string"
              }
            },
            "required": [
              "description",
              "headline",
              "id",
              "rel",
              "type"
            ],
            "type": "object"
          }
        ]
      },
      "type": "array"
    }
  }
}

使用https://www.jsonschemavalidator.net上的 JSON Schema 验证器,该模式会在数据正确呈现时进行验证,但在尝试捕获错误时不起作用。

在以下情况下,headline为视频添加了id. 这应该会失败,因为headline不允许在视频中使用,并且id是必需的。

{ 
  "media": [
    {
      "headline": "Yemeni mother arrives in U.S. to be with dying 2-year-old son",
      "rel": "primaryMedia",
      "type": "video"
    }
  ]
}

然而,我从验证器获得的结果并不完全是预期的。似乎它在响应中将两个对象模式混为一谈。

除此之外,我还单独发现该模式将允许在 中同时填充视频和图像对象media,这是意料之中的。

一直试图弄清楚我做错了什么,但我很难过。如果有人提供,将非常感谢一些反馈。提前致谢!


验证者响应:

消息:JSON 对来自“oneOf”的无模式有效。架构路径:#/properties/media/items/oneOf

消息:尚未定义属性“标题”,并且架构不允许其他属性。架构路径:#/properties/media/items/oneOf/0/additionalProperties

消息:枚举中未定义值“视频”。架构路径:#/properties/media/items/oneOf/0/properties/type/enum

消息:对象缺少必需的属性:描述、ID。架构路径:#/properties/media/items/oneOf/1/required

消息:对象缺少必需的属性:标题、id。架构路径:#/properties/media/items/oneOf/0/required

标签: jsonschemajson-schema-validator

解决方案


您的问题由三个部分组成,但前两个部分是相互关联的,所以我会解决这些问题,尽管它们本身没有“解决方案”。

  1. 如何验证数组中只有一个对象具有特定键,而其他对象没有。

你不能用 JSON Schema 做到这一点。适用于数组的 JSON Schema 关键字集没有表达“其中一个值必须与模式匹配”的方法,而是适用于数组中的所有项目,或者适用于数组中的特定项目数组(如果items是数组而不是对象)。 https://datatracker.ietf.org/doc/html/draft-handrews-json-schema-validation-01#section-6.4

  1. 验证输出不是我所期望的。我希望只看到与oneOf我的对象类型相关的失败分支,该对象类型由type键定义。

JSON Schema 规范(从 draft-7 开始)没有指定任何返回错误的格式,但是就它告诉您的内容而言,您得到的错误结构非常“完整”(并且类似于我们指定错误的方式应该是返回草稿8)。

考虑一下,就您的业务逻辑而言,验证器对您的架构或 JSON 实例一无所知。在验证 JSON 实例时,验证器可能会遍历所有值,并针对所有适用的子模式测试验证规则。查看您的错误,其中的两个模式oneOf都适用于数组中的所有项目,因此所有项目都经过验证。如果一个不满足条件,其他的也将被测试。验证器在使用 a 时无法知道oneOf您的意图是什么。

您可以通过使用if/then组合来解决此问题。如果您的if架构只是该类型的 const,并且您的then架构是完整的对象类型,您可能会得到更清晰的错误响应,但我没有测试过这个理论。

  1. 我希望数组中的所有项目都是其中一种类型。这里发生了什么?

从规格...

items

如果“items”是一个模式,则如果数组中的所有元素都成功地针对该模式进行验证,则验证成功。

oneOf

如果实例针对此关键字值
定义的一个模式成功验证,则该实例针对此关键字成功验证

您所做的是说:数组中的每个项目都应该根据 [schemaA] 有效。SchemA:根据以下模式之一,对象应该是有效的:[ oneOf] 中的模式。

当您认为“项目必须是以下项目之一”时,我可以看到这是多么令人困惑,但是items将值模式独立地应用于数组中的每个项目。

为了实现你的意思,移动oneOf上面的items,然后将其中一种媒体类型重构为另一种项目模式。

这是一个 sudo 模式。

oneOf: [items: properties: type: const: image], [items: properties: type: image]

如果有任何不清楚或您有任何后续问题,请告诉我。


推荐阅读