如何在 JSON 架构中扩展架构?

How to extend a schema in JSON schema?

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

我这样做是因为我希望所有模型都继承某些 structure/rules。例如,每个模型实例都应具有某些公共属性(例如,idcreatedAtupdatedAt)。在 OOP 术语中:Product extends 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. 如何指定文档元模式 "extends" 基本 JSON 模式 (draft-07),这样我就不必定义草稿的所有属性 ( $schemaid 等)?
  2. 如何指定每个模型架构的 properties 包含一些公共属性(idcreatedAt、...),而不必在每个模型中定义它们架构定义?

JSON 架构不使用面向对象的范例,因此继承等概念不能很好地翻译。 JSON 架构是约束的集合。它是减法而不是像大多数人习惯的那样加法。这意味着给定一个空模式,valid JSON 文档集是 all 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/override

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

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

进行验证

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

使用 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)

这至少解决了我的用例。