Skip to content

Keyword for extending a schema #907

Open
@awwright

Description

@awwright

I've been trying to implement $recursiveRoot and $recursiveRef myself, and it's pretty gnarly. I think we were trying to keep it super simple for implement, but it ended up having so many rules it's actually difficult to comprehend what's going on.

I have an idea for a different mechanism for "extending" recursive schemas, let's call it "$extends".

When a validator encounters the "$extends" keyword, it follows the reference to the schema with the given URI, and applies that schema as an applicator keyword, the same way "$ref" works—but with one change in behavior:

Any time a sub-instance applies the extended schema (because of a $ref or $extends recursive reference), the extending schema is also applied.

The mechanism to perform this is, I think, straightforward: the validation function just needs to accept an optional argument, a mapping of schema → set of schemas, by default whatever the parent value was, or an empty map (at the document root). The behavior: any validations against a schema in the map, will also validate that instance against all schemas in the mapped set.

This should even allow multiple recursive levels, for example, where one property recurses back to the parent, and a different property recurses up to the grandparent. As long as you use "$extends" for both cases, you should be able to write an extending schema that always gets applied when the base is; and so (afaict) unevaluatedProperties should still work properly.


An example for adventurous parties to wrap their heads around:

{
    $id: "base",
    type: "object",
    required: [ "id" ],
    properties: {
        "id": { type: "string" },
        "fancy": {
            $anchor: "fancy",
            type: "object",
            required: [ "children" ],
            properties: {
                "children": { items: { $ref: "#fancy" } },
                "schema": { $ref: "#" }
            }
        }
    }
}
{
    $id: "hyper",
    $extends: "base#",
    properties: {
        "links": { type: "array", items: { type: "string" } },
        "fancy": {
            $anchor: "fancy",
            type: "object",
            required: [ "rank" ],
            properties: {
                "rank": { type: "integer", minimum: 0 },
                "children": { items: { $ref:"#fancy", $extends: "base#fancy" } }
            }
        }
    }
}

An instance, validated against hyper:

{
    id: "A",
    links: [ "B", "C" ],
    fancy: {
        rank: 1,
        children: [
            {
                rank: 3,
                children: [
                    {
                        rank: 2,
                        schema: {
                            id: "B",
                            links: ["A"]
                        }
                    }
                ]
            }
        ],
        schema: { id: "C" }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions