Brett
Brett

Reputation: 941

any way to make a json schema extend from a allOf?

I am quite new to json schema's and I'm not finding a way to do this.

We are being forced to use draft-4 as we want to use Visual Studio to edit these

We have a json object that has essentially two parts

Part 1

Part 2

Is there any elegent way to express this in json schema or am I going to have to declare each Object and type by name and create 4 seperate schemas referring to each piece and then essentially have an verbosely expanded version of a oneOf that is essentially "oneof":[{aa},{ab},{ba},{bb}]


UPDATED

I've tried the suggestions but I can't seem to get this to work

Attempt 1. allOf\oneOf

{
    "$schema": "http://json-schema.org/draft-04/schema",
    "allOf": [
        {
            "oneOf": [
                {
                    "$ref": "#/definitions/directTableReference"
                },
                {
                    "$ref": "#/definitions/tableIdObject"
                }
            ]
        },
        {
            "oneOf": [
                {
                    "$ref": "#/definitions/ObjectA"
                },
                {
                    "$ref": "#/definitions/ObjectB"
                }
            ]
        }
    ],
    "definitions": {
        "directTableReference": {
            "type": "object",
            "title": "schema directTableReference",
            "properties": {
                "schema": {
                    "type": "string"
                },
                "tableName": {
                    "type": "string"
                }
            },
            "required": [
                "schema",
                "tableName"
            ]
        },
        "tableIdObject": {
            "type": "object",
            "title": "schema tableIdObject",
            "properties": {
                "tableId": {
                    "type": "string"
                }
            },
            "required": [
                "tableId"
            ]
        },
        "ObjectA": {
            "type": "object",
            "title": "schema objectA",
            "properties": {
                "ObjectAName": {
                    "type": "string"
                }
            },
            "required": [
                "ObjectAName"
            ]
        },
        "ObjectB": {
            "type": "object",
            "title": "schema objectB",
            "properties": {
                "ObjectBName": {
                    "type": "string"
                }
            },
            "required": [
                "ObjectBName"
            ]
        }
    }
}

This is incorrect but it is validated. I can see that it hasn't found ObjectAName to be ObjectA as it doesn't show the title in the hover over in visual studio code

{
    "$schema": "./testSchema.json",
    "ObjectAName":"blah",
    "ObjectBName":"blah"
}

Attempt 2 - oneOf\allOf with all 4 combinations

Even this doesn't work

{
    "$schema": "http://json-schema.org/draft-04/schema",
    "title": "testSchema",
    "oneOf": [
        {
            "allOf": [
                {
                    "$ref": "#/definitions/directTableReference"
                },
                {
                    "$ref": "#/definitions/ObjectA"
                }
            ],
            "additionalProperties": false
        },
        {
            "allOf": [
                {
                    "$ref": "#/definitions/tableIdObject"
                },
                {
                    "$ref": "#/definitions/ObjectA"
                }
            ],
            "additionalProperties": false
        },
        {
            "allOf": [
                {
                    "$ref": "#/definitions/directTableReference"
                },
                {
                    "$ref": "#/definitions/ObjectB"
                }
            ],
            "additionalProperties": false
        },
        {
            "allOf": [
                {
                    "$ref": "#/definitions/tableIdObject"
                },
                {
                    "$ref": "#/definitions/ObjectB"
                }
            ],
            "additionalProperties": false
        }
    ],
    "definitions": {
        "directTableReference": {
            "type": "object",
            "title": "schema directTableReference",
            "properties": {
                "schema": {
                    "type": "string"
                },
                "tableName": {
                    "type": "string"
                }
            },
            "required": [
                "schema",
                "tableName"
            ]
        },
        "tableIdObject": {
            "type": "object",
            "title": "schema tableIdObject",
            "properties": {
                "tableId": {
                    "type": "string"
                }
            },
            "required": [
                "tableId"
            ]
        },
        "ObjectA": {
            "type": "object",
            "title": "schema objectA",
            "properties": {
                "ObjectAName": {
                    "type": "string"
                }
            },
            "required": [
                "ObjectAName"
            ]
        },
        "ObjectB": {
            "type": "object",
            "title": "schema objectB",
            "properties": {
                "ObjectBName": {
                    "type": "string"
                }
            },
            "required": [
                "ObjectBName"
            ]
        }
    }
}

This is now seen as invalid and actually as the directTableReference Object

{
    "tableId": "blah",
    "ObjectAName": "blah"
}

Struggling to see the point of allOf when it basically makes impossible objects, but surely I'm missing a trick here.

Is there no way to actually reuse these objects or is there no way to do this in draft-4. Does it help if I can go to draft-7?

Thanks for any help

Upvotes: 0

Views: 1143

Answers (2)

Brett
Brett

Reputation: 941

Ok, @ether gave me confidence in the right direction and just writing this up here with the final actual schema in case it helps someone else trying to "merge" or "blend" json objects together with allOf

The solution was

  • definitions should only contain the "properties" and "required" parts finally construct the property options for our object with oneOf/allOf
  • use oneOf(allOf(Aa),allOf(Ab),allOf(Ba),allOf(Bb)) pattern
    • allOf(oneOf(A,B),oneOf(a, b)) would also work, however it means that you cannot provide a title to explain to the user which allowed property combination they are using, which was useful to our solution)
  • because we are using draft-4/7 we have to redefine the "properties" when combining the definitions to surface the inner definition properties. I understand this wouldn't be necessary with the later drafts

So the final working schema was this

{
    "$schema": "http://json-schema.org/draft-04/schema",
    "$id": "https://blah.com/test",
    "title": "testSchema",
    "oneOf": [
        {
            "title":"Ba",
            "allOf": [
                {
                    "$ref": "#/definitions/directTableReference"
                },
                {
                    "$ref": "#/definitions/ObjectA"
                }
            ],
            "properties": {
                "schema":{},
                "tableName":{},
                "ObjectAName":{}
            },
            "additionalProperties": false
        },
        {
            "title":"Aa",
            "allOf": [
                {
                    "$ref": "#/definitions/tableIdObject"
                },
                {
                    "$ref": "#/definitions/ObjectA"
                }
            ],
            "properties": {
                "tableId":{},
                "ObjectAName":{}
            },
            "additionalProperties": false
        },
        {
            "title":"Bb",
            "allOf": [
                {
                    "$ref": "#/definitions/directTableReference"
                },
                {
                    "$ref": "#/definitions/ObjectB"
                }
            ],
            "properties": {
                "schema":{},
                "tableName":{},
                "ObjectBName":{}
            },
            "additionalProperties": false
        },
        {
            "title":"Ba",
            "allOf": [
                {
                    "$ref": "#/definitions/tableIdObject"
                },
                {
                    "$ref": "#/definitions/ObjectB"
                }
            ],
            "properties": {
                "tableId":{},
                "ObjectBName":{}
            },
            "additionalProperties": false
        }
    ],
    "definitions": {
        "directTableReference": {
            "type":"object",
            "properties": {
                "schema": {
                    "title": "schema",
                    "type": "string"
                },
                "tableName": {
                    "title": "tableName",
                    "type": "string"
                }
            },
            "required": [
                "schema",
                "tableName"
            ]
        },
        "tableIdObject": {
            "type":"object",
            "properties": {
                "tableId": {
                    "title": "tableIdObject",
                    "type": "string"
                }
            },
            "required": [
                "tableId"
            ]
        },
        "ObjectA": {
            "type":"object",
            "properties": {
                "ObjectAName": {
                    "title": "ObjectA",
                    "type": "string"
                }
            },
            "required": [
                "ObjectAName"
            ]
        },
        "ObjectB": {
            "type":"object",
            "properties": {
                "ObjectBName": {
                    "title": "ObjectB",
                    "type": "string"
                }
            },
            "required": [
                "ObjectBName"
            ]
        }
    }
}

These are now valid

{
    "schema":"blah",
    "tableName": "blah",
    "ObjectAName": "blah"
}
{    
    "tableId": "blah",
    "ObjectAName": "blah",
}
{    
    "tableId": "blah",
    "ObjectBName": "blah",
}

and these are not

{    
    "tableId": "blah",
    "ObjectAName": "blah",
    "ObjectBName": "blah",
}
{    
    "tableId": "blah",
    "blah":"blah"
}

Upvotes: 1

Ether
Ether

Reputation: 53996

Sure, you should be able to do that by combining definitions and $ref with allOf, anyOf and oneOf.

https://json-schema.org/understanding-json-schema/reference/conditionals.html
https://json-schema.org/understanding-json-schema/structuring.html

The docs refer to $defs, but in draft4 that was called definitions.

So, something like:

definitions:
  tableId:
    ...
  Schema:
    ...
  ObjectA:
    ...
  ObjectB:
    ...
  ObjectC:
    ...
allOf:
  - anyOf:
    - $ref: '#/definitions/tableId'
    - allOf:
      - $ref: '#/definitions/Schema'
      - $ref: '#/definitions/TableName'
  - anyOf:
    - allOf:
      - $ref: '#/definitions/ObjectA'
      - $ref: '#/definitions/ObjectB'
      - $ref: '#/definitions/ObjectC'
    - allOf:
      - $ref: '#/definitions/ObjectA'
      - $ref: '#/definitions/ObjectD'

Upvotes: 1

Related Questions