How to do inheritance?
kiloquad opened this issue · 4 comments
Sorry, I can't find any better place to ask and I can't find any examples. It looks like $merge would do it but according to issue #15, this wont be included.
My use case, I have an Person
object in a schema file with a firstName
and lastName
requires firstName
.
I need an Employee
object in a separate schema. I want it to inherit Person
and add the property company
and require the company
property.
A valid Person
:
{
"firstName": "Bob",
"lastName": "Smith"
}
An invalid Person
:
{
"firstName": "Bob"
}
A invalid Person
:
{
"firstName": "Bob",
"lastName": "Smith",
"foo": "bar"
}
A valid Employee
:
{
"firstName": "Bob",
"lastName": "Smith",
"company": "ABC Inc."
}
An invalid Employee
:
{
"firstName": "Bob",
"lastName": "Smith",
"company": "ABC Inc.",
"foo": "bar"
}
A invalid Employee
:
{
"firstName": "Bob",
"company": "ABC Inc."
}
I'd like to end up with two schema files, Person.schema.json
and Employee.schema.json
. Where a Person
object or and Employee
object would pass Person.schema.json
validation, a Person
object would not necessarily pass Employee.schema.json
validation.
JSON Schema doesn't support inheritance. You can combine schemas using boolean operations in ways that can look like inheritance, but if you think about it that way, you will confuse what is actually happening. You will have to get out of that mindset and think about defining your data a little differently.
Your example is a fairly easy one to express except for one thing. I'll start with the schemas and then explain what it's doing and what the limitations are.
{
"id": "http://example.com/schemas/person",
"type": "object",
"properties": {
"firstName": { "type": "string" },
"lastName": { "type": "string" }
},
"required": ["firstName", "lastName"]
}
{
"id": "http://example.com/schema/employee",
"allOf": [{ "$ref": "http://example.com/schemas/person" }],
"properties": {
"company": { "type": "string" }
},
"required": ["company"]
}
In the Employee schema, we use allOf
to say that all of the constraints that apply to Person also apply to Employee. When we do this, nothing overrides anything else. When there are properties that appear in both schemas, both apply.
{
"type": "object",
"allOf": [
{
"properties": {
"foo": { "type": "integer", "minimum": 2 }
}
}
{
"properties": {
"foo": { "type": "integer", "multipleOf": 2 }
}
}
]
}
In this example the property "foo" has a minimum of 2 and excludes odd numbers.
The limitation you have with this approach is that you can't use "additionalProperties": false
. Imagine if I added that constraint to the Person schema. Because all keywords apply when combining schemas, there would be no JSON value that could ever be valid against that schema. The Person schema is asserting that there can be no properties other than "firstName" and "lastName", but the Employee schema is asserting that the "company" schema is required. Here is one way around the problem.
{
"id": "http://example.com/schema/final-employee",
"allOf": [
{ "$ref": "http://example.com/schemas/person" },
{ "$ref": "http://example.com/schemas/employee" }
],
"properties": {
"firstName": {},
"lastName": {},
"company": {}
},
"additionalProperties": false
}
The problem, of course, is that any time you add a property to either of these schemas, you need update FinalEmployee as well. That's why I advise that you always ignore additional properties rather than explicitly forbid them. It has some value, but not nearly enough to be worth the pain.
So, I hope that helped. Schemas are not describing objects, they are a collection of constraints. So, thinking in terms of inheritance doesn't work.
@kiloquad see also json-schema-org/json-schema-org.github.io#77 for a more verbose but also more flexible approach using the draft-06 keyword propertyNames
I took the re-use/addlProps label off because I'm using that to manage an informal vote on the various proposed solutions, and this is a question rather than solution. If you think the answer is found somewhere among the existing solution proposals, please vote per the VOTE-A-RAMA comment and close this.
It's been nearly two weeks since this was answered, with no further questions. I've filed an issue on the web site repo to document this better, so I'm going to close this out. Please continue any related conversation at json-schema-org/json-schema-org.github.io#148