json-schema-viewer icon indicating copy to clipboard operation
json-schema-viewer copied to clipboard

Support for merging property schema with single allOf subschema

Open tobinus opened this issue 3 years ago • 1 comments

Sometimes you need to define a type of field once, and add a default property that may be different whenever this type is used. One way to achieve this is to follow the Factoring Schemas example in the Understanding JSON Schema book, except you use one allOf subschema instead of multiple ones.

For instance:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "One type, multiple uses",
  "description": "Demonstration of a use case where a type is defined once, and included multiple times",
  "type": "object",
  "properties": {
    "titleFiltering": {
      "default": "off",
      "allOf": [
        {
          "$ref": "#/definitions/FilterOptions"
        }
      ]
    },
    "subtitleFiltering": {
      "default": "off",
      "allOf": [
        {
          "$ref": "#/definitions/FilterOptions"
        }
      ]
    },
    "idFiltering": {
      "default": "if present",
      "allOf": [
        {
          "$ref": "#/definitions/FilterOptions"
        }
      ]
    }
  },
  "additionalProperties": false,
  "definitions": {
    "FilterOptions": {
      "title": "FilterOptions",
      "description": "Options for when filtering is done.",
      "enum": [
        "off",
        "if present",
        "required"
      ],
      "type": "string"
    }
  }
}

JSON Schema Viewer does not support this, and ignores the contents of the allOf property when documenting the properties. However, it is clearly being read, because the examples are all set to off, and the type is correctly identified as string:

bilde

I tried to test different ways of achieving this:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Testing singular allOf support",
  "description": "\"ref inside allOf\" and \"inline inside allOf\" should ideally give the same visual result as \"inline directly\", since they express the same thing. Note that \"ref directly\" is not a valid substitute, since the default value is missing.",
  "type": "object",
  "properties": {
    "inline directly": {
      "title": "FilterOptions",
      "description": "Options for how filtering is done.",
      "enum": [
        "off",
        "if present",
        "required"
      ],
      "type": "string",
      "default": "off"
    },
    "inline inside allOf": {
      "default": "off",
      "allOf": [
        {
          "title": "FilterOptions",
          "description": "Options for how filtering is done.",
          "enum": [
            "off",
            "if present",
            "required"
          ],
          "type": "string"
        }
      ]
    },
    "ref directly": {
      "default": "off",
      "$ref": "#/definitions/FilterOptions"
    },
    "ref inside allOf": {
      "default": "off",
      "allOf": [
        {
          "$ref": "#/definitions/FilterOptions"
        }
      ]
    }
  },
  "additionalProperties": false,
  "definitions": {
    "FilterOptions": {
      "title": "FilterOptions",
      "description": "Options for how filtering is done.",
      "enum": [
        "off",
        "if present",
        "required"
      ],
      "type": "string"
    }
  }
}

Result in JSON Schema Viewer:

bilde

"ref inside allOf" and "inline inside allOf" should ideally give the same visual result as "inline directly", since they express the same thing. If support for this was implemented, it would make JSON Schema Viewer more useful and reduce the need for repeating yourself. Pydantic outputs JSON Schema like this when a field is set to an enum and you define a default value for that enum (see samuelcolvin/pydantic#2592 for an example).

Since it doesn't help to inline the $ref, the json-schema-ref-parser tool is not going to be of any help here on its own. The only workaround I can think of that would not involve changing JSON Schema Viewer, would be to add some post-processing to the JSON Schema that inlines the $ref and then identifies instances of allOf with a single subschema, and merged that subschema with the parent schema, eliminating the allOf.

tobinus avatar Jan 05 '22 09:01 tobinus

Hey @tobinus, you could try experimenting with this package to merge the allOfs. It's what we're using to "normalize" our schemas before displaying them with the viewer! https://github.com/mokkabonna/json-schema-merge-allof

julrich avatar Mar 22 '22 08:03 julrich