【问题标题】:How to represent union types in JSON schema validator?如何在 JSON 模式验证器中表示联合类型?
【发布时间】:2020-07-28 04:11:30
【问题描述】:

我是 JSON 模式验证的新手,我正在为配置构建自定义模式。我正在构建的架构基于 Typescript 类型。我了解如何验证简单的数据类型,如数组、对象、数字、字符串等。

但是有没有办法像这样指定类型:

type Conf = {
idle_session_timeout?: number | "none",
item: {
    kind: "attribute";
    name: string;
} | {
    kind: "relation";
    name: string;
} | {
    kind: "group";
    name: string;
    label?: string | undefined;
    entries: PresentationItem[];
}
 order_by: string | {
    attribute: string;
    direction?: "asc" | "desc" | undefined;
}
}

我从http://json-schema.org/draft-07/schema 注意到它支持 if then else 语句来根据值切换验证模式,但我不知道如何实现它们。

【问题讨论】:

  • 对于复杂类型,anyOf 应该是您要查找的内容。对于像order_by.direction 这样的简单枚举,enum 可能会派上用场。
  • 您不必从头开始编写这些模式。有用于从 Typescript 生成 JSON Schema 的现有包。您可能想查看其中的一个,并从它们产生的内容中学习,甚至只在之后通过标题和描述等来增强它们。

标签: jsonschema json-schema-validator


【解决方案1】:

您需要注意几个关键字,并且可能参考规范:

首先,“type”允许在一个数组中指定多个值。有了这个,你可以指定例如["string", "number"] 表示“字符串或数字”。许多关键字仅在实例属于某种 JSON 类型时才适用。通常,如果所有剩余的关键字仅适用于各自的类型,您可以将一个“类型”的架构和另一个具有不同“类型”的架构组合起来。

因此,例如,您可以有两个架构,例如:

{
  "type": "string",
  "minLength": 1
}
{
  "type": "number",
  "minimum": 0
}

因为“minimum”只适用于数字,而“minLength”只适用于字符串,所以你可以简单地将模式组合在一起,效果是一样的:

{
  "type": ["string", "number"],
  "minLength": 1
  "minimum": 0
}

但是,对于相同“类型”的两个模式,这样做将执行 交集 而不是联合。这是因为向 JSON Schema 添加关键字会添加约束,而向“类型”列表添加值会删除约束(更多值变得有效)。

因此,如果您对相同“类型”的两个模式执行联合,或者如果您将模式与跨所有类型(特别是“枚举”或“常量”)验证的关键字组合,则需要将它们组合使用“anyOf”关键字,它对多个模式的数组执行联合。 (您也可以考虑“oneOf”。)


我认为您最终会得到这样的架构:

{
"type": "object",
"properties": {
  "idle_session_timeout": {
    "type": ["number","string"],
    "anyOf": [ {"type":"number"}, {"const":"none"} ]
  },
  "item": {
    "type": "object",
    "required": ["kind", "name"],
    "properties": {
      "kind": { "type": "string" },
      "name": { "type": "string" },
    },
    "anyOf": [
      {
        "properties": {
          "kind": { "const": "attribute" },
        }
      },
      {
        "properties": {
          "kind": { "const": "relation" },
        }
      },
      {
        "required": ["entries"],
        "properties": {
          "kind": { "const": "group" },
          "label": { "type": "string" },
          "entries": { "type":"array", "items": {"$ref":"PresentationItem"} },
        }
      }
    ]
  },
  "order_by": {
    "type": ["string", "object"],
    "required": ["attribute"],
    "properties": {
      "attribute": { "type": "string" },
      "direction": { "enum": ["asc", "desc"] },
    }
  }
}

请注意,我是如何将“anyOf”中的常用关键字分解为可能的最高级别的。这是一种风格选择。它会产生稍微干净的错误,但它可能与您无关,具体取决于您计划如何扩展架构。

【讨论】:

  • 清除错误实际上是非常相关的,因为我只共享了类型声明的一小部分。该配置由一个具有 30 个属性的大对象组成,其中包含数组、对象数组和嵌套对象。所以很可能会定义一些子模式并将它们添加到$ref
猜你喜欢
  • 2020-05-20
  • 1970-01-01
  • 2019-11-06
  • 2021-09-01
  • 2016-05-28
  • 1970-01-01
  • 1970-01-01
  • 2021-11-28
  • 2012-10-23
相关资源
最近更新 更多