Skip to content

Options for for dealing with custom JSON Schema dialects in the OAS 3.1 meta-schema #2485

@jdesrosiers

Description

@jdesrosiers

OpenAPI 3.1 allows users to change the JSON Schema dialect that they want to use. However, this poses a problem with the schema that describes OpenAPI. If the schema dialect is not fixed, the OpenAPI schema doesn't know which dialect to validate any given schema against.

I've come up with three possible ways to deal with this problem.

Option 1 - Don't validate JSON Schemas

The simplest way is to not validate JSON Schemas with the OpenAPI schema. Validating the JSON Schemas within the OpenAPI document would have to be done in separate step.

Option 2 - $dynamicRef

$dynamicRef is a new keyword in JSON Schema 2020-12 that can be used to address this problem as long as you constrain yourself to one dialect at a time in your OpenAPI document. Here's an example of what a developer would have to do to extend the OpenAPI schema to use straight JSON Schema 2020-12 instead of the default OAS 3.1 dialect.

In addition to extending the OpenAPI schema in the normal way (with $ref), it overrides the Schema object by setting the $dynamicAnchor. We also lockdown the dialect so it can't be overridden by forcing the jsonSchemaDialect in the OpenAPI document and the $schema JSON Schema keyword to only allow JSON Schema 2020-12 to be used.

{
  "$id": "https://json-schema.hyperjump.io/openapi-w-202012",
  "$schema": "https://json-schema.org/draft/2020-12/schema",

  "$ref": "https://spec.openapis.org/oas/3.1/schema/2021-02-18",
  "properties": {
    "jsonSchemaDialect": { "$ref": "#/$defs/Dialect" }
  },
  "required": ["jsonSchemaDialect"],

  "$defs": {
    "Dialect": { "const": "https://json-schema.org/draft/2020-12/schema" },
    "Schema": {
      "$dynamicAnchor": "meta",
      "$ref": "https://json-schema.org/draft/2020-12/schema",
      "properties": {
        "$schema": { "$ref": "#/$defs/Dialect" }
      }
    }
  }
}

The following is a truncated version of what the OpenAPI schema would need to look like to use this strategy. The important part to note is that the Schema is referenced using $dynamicRef instead of $ref and that the $dynamicAnchor that the $dynamicRef points to can be overridden by an extending schema.

{
  "$id": "https://spec.openapis.org/oas/3.1/schema/2021-02-18",
  "$schema": "https://json-schema.org/draft/2020-12/schema",

  "type": "object",
  "properties": {
    "components": { "$ref": "#/$defs/Components" }
  },

  "$defs": {
    "Components": {
      "type": "object",
      "properties": {
        "schemas": {
          "type": "object",
          "additionalProperties": { "$dynamicRef": "#meta" }
        }
      }
    },
    "Schema": {
      "$dynamicAnchor": "meta",
      "$ref": "https://spec.openapis.org/oas/3.1/dialect/base"
    }
  }
}

You can try this out at https://json-schema.hyperjump.io. Put the extending schema in the first "Schema" tab and the truncated OpenAPI schema in a second "Schema" tab. Then put an OpenAPI document in an "Instance" tab to see how it validates. The following is one of my tests. The discriminator field is invalid, but it passes validation because it's validating against JSON Schema 2020-12 which ignores the discriminator because that keyword doesn't exist JSON Schema 2020-12.

{
  "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
  "components": {
    "schemas": {
      "foo": {
        "discriminator": false
      }
    }
  }
}

Option 3 - OpenAPI as JSON Schema dialect

I'm not talking about the Schema Object dialect. I'm talking about all of OpenAPI being approached as a non-validation JSON Schema dialect. JSON Schema 2020-12 added clarification about embedding schemas with $id and how to handle references that point to schemas with a different dialect than the parent schema.

{
  "$id": "https://json-schema.hyperjump.io/example1",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "allOf": [
    {
      "type": "number",
      "exclusiveMaximum": 3
    },
    {
      "$id": "https://json-schema.hyperjump.io/example2",
      "$schema": "http://json-schema.org/draft-04/schema#",
      "type": "number",
      "maximum": 3,
      "exclusiveMaximum": true
    }
  ]
}

/allOf/1/$id indicates that the schema defined there should be evaluated as it's own standalone schema using it's $schema to determine how to interpret its keywords. That's exactly the kind of behavior we would like with OpenAPI schema validation. The problem is that this only works for meta-validation, not validating an instance against a schema.

That means that to take advantage of this functionality, we would have to treat the OpenAPI document as a whole as a JSON Schema dialect. It might sound strange to have a JSON Schema dialect that describes something other than a JSON instance, but it's well within the bounds of something you can do with a JSON Schema Vocabulary. I could use the standard vocabulary tools I already have available in Hyperjump JSC to build this in an hour or so and get full meta-validation no matter which schema dialect is chosen (even using several at once) and an AST of the compiled OpenAPI document (no bundling step necessary) ready to be processed for things like code or documentation generation.

A side effect of doing this is that you automatically get all of the JSON Schema Core keywords by virtue of being a JSON Schema Vocabulary. That means you can use keywords like $anchor in your OpenAPI document even tho it's not part of the spec.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions