【问题标题】:How to extend a schema in JSON schema?如何在 JSON 模式中扩展模式?
【发布时间】:2019-03-05 02:36:55
【问题描述】:

我正在使用 JSON 模式进行数据建模。我定义了一个基本的Document 模式,稍后我用它来定义模型模式(例如ProductCategoryUser 等)。

我这样做是因为我希望所有模型都继承某些结构/规则。例如,每个模型实例都应该具有某些公共属性(例如,idcreatedAtupdatedAt)。在 OOP 术语中:Product extends Document,因此它继承了它的实例属性。在模式术语中(我认为)Document 是用于创建模型模式的元模式。

我已将 Document 架构定义如下:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "id": "http://example.com/schemas/document.json#",
  "title": "Document",
  "type": "object",
  "additionalProperties": false,
  "required": ["type", "name", "fields"],
  "properties": {
    "type": {
      "constant": "document"
    },
    "name": {
      "type": "string"
    },
    "title": {
      "type": "string"
    },
    "description": {
      "type": "string"
    },
    "readOnly": {
      "type": "boolean"
    },
    "properties": {
      // common properties 
      // model-specific properties
    }
  }
}
  1. 如何指定文档元模式“扩展”基本 JSON 模式 (draft-07),这样我就不必定义草稿的所有属性 ($schema, id,等)?
  2. 如何指定每个模型架构的properties 包含一些公共属性(idcreatedAt、...),而不必在每个模型架构定义中定义它们?

【问题讨论】:

    标签: json schema jsonschema


    【解决方案1】:

    JSON Schema 不使用面向对象的范例,因此继承等概念不能很好地转换。 JSON Schema 是一组约束。它是减法而不是像大多数人习惯的那样加法。这意味着给定一个空模式,有效 JSON 文档的集合是所有 JSON 文档的集合。当您添加关键字时,您正在从一组有效的 JSON 文档中减去。一旦从集合中移除某些东西,就无法重新添加。

    因此,您可以使用组合来“扩展”架构,但永远不能“覆盖”另一个架构定义的内容。

    让我们看一个没有冲突属性的简单扩展示例。

    /schema/base

    {
      "type": "object",
      "properties": {
        "foo": { "type": "string" },
        "bar": { "type": "string" }
      }
    }
    

    /schema/extended

    {
      "allOf": [{ "$ref": "/schema/base" }],
      "properties": {
        "baz": { "type": "string" }
      }
    }
    

    这适用于 JSON Schema。现在让我们看一个属性定义冲突的示例。

    /schema/override

    {
      "allOf": [{ "$ref": "/schema/base" }],
      "properties": {
        "bar": { "type": "integer" },
        "baz": { "type": "boolean" }
      }
    }
    

    在本例中,两个模式都有一个/properties/bar 字段。如果您从继承的角度考虑这一点,您将误解这里发生的事情。在这种情况下,both“/properties/bar”字段必须有效。没有冲突需要解决。正如关键字所说,“所有”模式必须有效。由于bar 不可能既是整数又是字符串,因此没有文档会针对/schema/override 进行验证。

    希望这可以为您提供足够的信息来解决您的问题并避免最常见的问题。

    【讨论】:

    • 当你写“在这种情况下,两个“/properties/bar”字段都必须有效”时,你的意思是bar必须都是字符串(根据基本模式)and 是一个整数(根据覆盖模式)?既然这是不可能的,任何 JSON 都会在覆盖模式上失败,不是吗?
    • 没错。一个不优先于另一个。他们都适用。由于没有可能既是字符串又是整数的 JSON 文档,因此对于该模式,没有任何东西是有效的。
    • requiredproperties(或任何其他关键字)的作用相同。它只是添加到约束包中,无论它来自基本模式还是扩展模式。如果有多个requireds,它们都适用。 (也许你的意思是,这如何与 "additionalProperties": false 一起工作?这就是事情变得奇怪的地方。)
    • 应该注意的是,官方指南特别指出这个建议没有实际上扩展任何东西 -- web.archive.org/web/20201020173257/https://json-schema.org/…
    • @Patrick 如果您使用的是 2019-09 或更高版本的草案,那么您可以省略 allOf。在 JSON Schema 的早期草稿中,如果一个对象中有一个 $ref,它被认为只是一个引用而不是一个模式。这意味着该参考中的任何 JSON Schema 关键字都会被忽略,因为关键字仅在 Schema 中有意义。这实际上意味着$ref 必须始终单独,我们需要allOf 来组合引用和模式。在新版本的$ref 中,引用是$ref 关键字的字符串值,$ref 的工作方式类似于具有一个架构的allOf
    【解决方案2】:

    在使用 NodeJs 时,使用ajv 验证器可以很简单地绕过简单模式的限制,如下所示:

    function extendJsonSchema(baseSchema, extendingSchema) {
        let extendedSchema = Object.assign({}, extendingSchema);
        extendedSchema.properties = Object.assign(extendedSchema.properties, baseSchema.properties)
        extendedSchema.required = extendedSchema.required.concat(baseSchema.required)
        return extendedSchema
    }
    
    
    let baseSchema = require('./base.schema.json')
    let extendingSchema = require('./extending.schema.json')
    
    let extendedSchema = extendJsonSchema(baseSchema, extendingSchema)
    const validate = ajv.compile(extendedSchema)
    

    这至少解决了我的用例。

    【讨论】:

      猜你喜欢
      • 2022-07-05
      • 2018-06-05
      • 2023-03-03
      • 2019-12-06
      • 2023-03-17
      • 2018-12-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多