Search code examples
jsonschema

How to define in JSON Schema an object property that holds an unknown JSON Schema?


What would be a JSON Schema for an object of this model:

{
    "mySchema": {
        // any valid JSON Schema
    }
}

I know you can define the property as an object, like this:

{
    "type": "object",
    "properties": {
        "mySchema": {
            "type": "object"
        }
    }
}

But can you define the value of mySchema as an unknown JSON Schema, but not a specific JSON Schema, and not only an object?

Here are the specs I was looking, https://json-schema.org/draft/2020-12/json-schema-core.html. It's not clear to me whether this is legit e.g.

{
    "type": "object",
    "properties": {
        "mySchema": {
            "type": "object",
            "$schema": "https://json-schema.org/draft/2020-12/schema"
        }
    }
}

Solution

  • In JSON Schema, we have a concept of a "meta-schema". This is a schema that you can use to validate that a value is a schema. The meta-schema for any given version of JSON Schema can be referenced using the dialect URI you use in $schema to declare what version of JSON Schema your using.

    {
      "type": "object",
      "properties": {
        "mySchema": {
          "$ref": "https://json-schema.org/draft/2020-12/schema"
        }
      }
    }
    

    However, there is one limitation. If the schema uses $id to include embedded schemas and any of those embedded schemas use a different version of JSON Schema than the root schema, it might not work properly. For example, if you have draft-07 schema inside a 2020-12 schema, the meta-schema will validate the whole thing as a 2020-12 schema and not recognize the change in dialect.

    {
      "$id": "https://example.com/schema",
      "$schema": "https://json-schema.org/draft/2020-12/schema",
      "type": "object",
      "properties": {
        "foo": {
          "$id": "/embedded",
          "$schema": "http://json-schema.org/draft-07/schema#",
          "type": "array",
          "items": [
            { "type": "string" }
          ]
        }
      }
    }
    

    This is a perfectly valid schema, but it wouldn't pass validation against the 2020-12 meta-schema. The array form of items is allowed in draft-07, but the meta-schema validates it as a 2020-12 schema where the array form of items was removed. It's possible to work around this limitation with a custom meta-schema, but that would get nasty and it's probably not worth the effort.

    If you want to support not just any JSON Schema, but any version of JSON Schema, you can use anyOf and list all of the versions you support.

    {
      "type": "object",
      "properties": {
        "mySchema": {
          "anyOf": [
            { "$ref": "https://json-schema.org/draft/2020-12/schema" },
            { "$ref": "http://json-schema.org/draft-07/schema#" }
          ]
        }
      }
    }
    

    This doesn't solve the problem I described. With this schema, the "mySchema" value will be accepted if the schema validates against 2020-12 or draft-07, but still can't use both for the same validation or know when to switch from one the other.