Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to do inheritance? #348

Closed
kiloquad opened this issue Aug 16, 2017 · 4 comments
Closed

How to do inheritance? #348

kiloquad opened this issue Aug 16, 2017 · 4 comments
Milestone

Comments

@kiloquad
Copy link

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.

@jdesrosiers
Copy link
Member

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": "https://example.com/schemas/person",
  "type": "object",
  "properties": {
    "firstName": { "type": "string" },
    "lastName": { "type": "string" }
  },
  "required": ["firstName", "lastName"]
}
{
  "id": "https://example.com/schema/employee",
  "allOf": [{ "$ref": "https://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": "https://example.com/schema/final-employee",
  "allOf": [
    { "$ref": "https://example.com/schemas/person" },
    { "$ref": "https://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.

@handrews
Copy link
Contributor

@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

@handrews handrews added this to the draft-07 (wright-*-02) milestone Aug 18, 2017
@handrews
Copy link
Contributor

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.

@handrews
Copy link
Contributor

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants