bounding (v5 purposal) - sgpinkus/json-schema GitHub Wiki

THIS WIKI IS OBSOLETE. PLEASE SEE THE NEW JSON-SCHEMA-ORG/JSON-SCHEMA-SPEC REPOSITORY.


NOTE: This proposal has been incorporated into https://github.com/json-schema-org/json-schema-spec/issues/64


Proposed keywords

  • bounding

Problem

When you use OneOf or AnyOf grouping you can't easily determine is there error with fields inside one of nested schemas or actually none of the nested schemas is matching object.

You could consider http://json-schema.org/example2.html as the good example illustrating complex multi-schema pattern divided by the type: [enum] properties.

Purpose

The purpose of the bounding keyword is to limit validation and errors generation to the schema OneOf or AnyOf branch when all key (bounding) properties have been matched inside it.

Values

The bounding is an option boolean flag with default false value.

Concerns

This keyword introduces a completely new concept in the design principles of JSON-schema. Currently the keywords are either fully independent of each other or only dependent on the keywords on the same level of the schema (minimum/exclusiveMinimum, items/additionalItems, etc.). If bounding is introduced into the standard the keywords anyOf/oneOf will behave differently depending on the bounding keyword on any level inside the contained schemas. Not only it is difficult to implement, it introduces potential ambiguity and contradictions: what if this keyword is encountered more than once? what if it binds more than one schema? or if it is in refs inside refs? Does it mean they have to be all resolved?

The suggestion is to consider alternative solutions to this problem. Maybe just to improve error reporting? It seems like this is an issue of a particular validator rather than JSON-schema standard.

For example if you use Ajv, you will get these errors (reduced to messages only): "data.record should have required property 'age', data.record.type should be equal to one of values, data.record should have required property 'pages', data.record should match exactly one schema in oneOf" (which is slightly better than the example below). But if you rewrite the schema using switch keyword as in the example in tonic the error message is simply: "data.record should have required property 'age'" that is exactly what bounding is trying to achieve.

Validation

For object that have properties with bounding set as true data record first of all is checking against this properties ignoring all other and if it matches no other objects with the same level is used for validation and validation continues bounded by this object.

  • if object in the OneOf or AnyOf scope have any property with bounding: true then make pre-validation using only fields with bounding:true of this object.
    • if any of the fields with bounding: true fails continue validation as usual.
    • if all properties of given object with bounding: true defined is matches (respecting required) then continue validate fields and nested objects:
      • if object validation is successful continue as usual.
      • if object validation fails no any other neighbour OneOf is used for the validation and only this object validation errors is raised. No matching schema error is not raised.

Example

{
    "id": "http://some.site.somewhere/entry-schema#",
    "$schema": "http://json-schema.org/draft-04/schema#",
    "description": "schema for an fstab entry",
    "type": "object",
    "required": [ "record" ],
    "properties": {
        "record": {
            "type": "object",
            "oneOf": [
                { "$ref": "#/definitions/student" },
                { "$ref": "#/definitions/book" }
            ]
        }
    },
    "definitions": {
        "student": {
            "properties": {
                "type": {
                    "enum": [ "student" ],
                    "bounding": true
                },
                "age": {
                    "type": "integer"
                }
            },
            "required": [ "age" ],
            "additionalProperties": false
        },
        "book": {
            "properties": {
                "type": {
                    "enum": [ "book" ],
                    "bounding": true
                },
                "pages": {
                    "type": "integer"
                }
            },
            "required": [ "pages" ],
            "additionalProperties": false
        }
    }
}

The valid data samples is:

{ 
    "record": {
        "type": "book",
        "pages": 1
    }
}
{ 
    "record": {
        "type": "student",
        "age": 18
    }
}

but if you use v4 schema against:

{ 
    "record": {
        "type": "student"
    }
}

You will get uninformative that provide very little help when you trying to understand why schema has failed.

Message:
JSON is valid against more than one schema from 'oneOf'. No valid schemas.
Schema path:
http://some.site.somewhere/entry-schema#/properties/record/oneOf
    Message:
    Value "book" is not defined in enum.
    Schema path:
    http://some.site.somewhere/entry-schema#/definitions/student/properties/type/enum
    Message:
    Required properties are missing from object: pages.
    Schema path:
    http://some.site.somewhere/entry-schema#/definitions/book/required
    Message:
    Required properties are missing from object: age.
    Schema path:
    http://some.site.somewhere/entry-schema#/definitions/student/required

When bounding flag is used error output should looks like:

    Message:
    Required properties are missing from object: age.
    Schema path:
    http://some.site.somewhere/entry-schema#/definitions/student/required

This is more informative and significally better for further automatic errors output and highlighting.