Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to extend a schema in JSON schema?

I'm using JSON schema for data modelling. I define a base Document schema, that I later use to define model schemas (e.g. Product, Category, User, etc.).

I'm doing this because I want all models to inherit certain structure/rules. For example every model instance should have certain common properties (such as, id, createdAt, updatedAt). In OOP terminology: Product extends Document and therefore it inherits its instance properties. In schemas terminology (I think) Document is a meta-schema for creating model schemas.

I've defined the Document schema as follows:

{
  "$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. How do I specify that the Document meta-schema "extends" the base JSON schema (draft-07), so that I don't have to define all the properties of the draft ($schema, id, etc.)?
  2. How do I specify that the properties of each model schema contains some common properties (id, createdAt, ...), without having to define them in each model schema definition?
like image 216
Panagiotis Panagi Avatar asked Sep 29 '18 08:09

Panagiotis Panagi


People also ask

What is JSON Schema file extension?

A JSON Schema is a valid JSON file so the extension . json is OK. Then, the first attribute of your file should be '$schema' to declare the version of the specification you are using.

What is additional properties in JSON Schema?

The additionalProperties keyword is used to control the handling of extra stuff, that is, properties whose names are not listed in the properties keyword or match any of the regular expressions in the patternProperties keyword. By default any additional properties are allowed.

How do I set a schema in JSON?

At the top of the file, you can specify the schema's id, the schema that should be used to validate the format of your schema, and a descriptive title. These are all defined using the keywords id, $schema and title, all of which are provided in the draft JSON Schema.

What does Exclusiveminimum property in JSON Schema mean?

Gets or sets a flag indicating whether the value can not equal the number defined by the minimum attribute (Minimum).


2 Answers

JSON Schema doesn't use an object oriented paradigm, so concepts like inheritance don't translate well. JSON Schema is a collection of constraints. It's subtractive rather than additive like most people are used to. This means that given an empty schema, the set of valid JSON documents is the set of all JSON documents. As you add keywords, you are subtracting from the set of valid JSON documents. Once something is removed from the set, it can't be added back in.

Therefore, you can use composition to "extend" a schema, but you can never "override" something that another schema defines.

Let's look at a simple extension example with no conflicting properties.

/schema/base

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

/schema/extended

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

That works great with JSON Schema. Now let's look at an example with conflicting property definitions.

/schema/override

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

In this example, both schemas have a /properties/bar field. If you are thinking about this in terms of inheritance, you're going to misunderstand what is happening here. In this case, both "/properties/bar" fields must be valid. There is no conflict to resolve. As the keyword says, "all of" the schemas must be valid. Since bar can't possibly be both an integer and a string, no document will ever validate against the /schema/override.

Hopefully that gives you enough information to solve your problem and avoid the most common gotcha.

like image 160
Jason Desrosiers Avatar answered Oct 20 '22 05:10

Jason Desrosiers


When using NodeJs, it is straightforward to get around this limitation on simple schemas with a little code using ajv validator like this:

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)

This solves my use-case at least.

like image 37
Gudlaugur Egilsson Avatar answered Oct 20 '22 03:10

Gudlaugur Egilsson