Description
We are coming up against issues with additionalProperties="false"
(as documented very well in other issues) and in conversation on the json-schema slack workspace @Relequestual suggested that we provide examples here so we could check whether the changes coming in draft-08 (#556) would solve our problems.
We're from IPTC, an international standards body for the media industry, trying to make a JSON version of our existing XML-based standard SportsML. Ideally the JSON version will be semantically compatible with the XML version.
The XML Schema version uses a lot of inheritance to cover properties that are common to many elements in sports data: people, places, statistics, venues etc.
To go back to the simplest example, and avoiding sports for now, take the following schema, simple-schema.json
. We have a teacher and a student object, both of which inherit shared properties from personProperties. We include the shared properties using the allOf
construct.
{
"$id": "simple-schema.json#",
"$schema": "http://json-schema.org/draft-06/schema#",
"description": "Really simple schema",
"additionalProperties": false,
"type": "object",
"properties": {
"student": {
"$ref": "#/definitions/student"
},
"teacher": {
"$ref": "#/definitions/teacher"
}
},
"definitions": {
"personProperties": {
"description": "Properties shared by all people.",
"properties": {
"date-of-birth": {
"type": "string",
"description": "Date of the person's birth."
},
"name": {
"type": "string",
"description": "The person's name."
}
}
},
"student": {
"type": "object",
"allOf": [{
"$ref": "#/definitions/personProperties"
}],
"properties": {
"graduation-year": {
"type": "string",
"description": "Year in which the student is expected to graduate."
}
}
},
"teacher": {
"type": "object",
"allOf": [{
"$ref": "#/definitions/personProperties"
}],
"properties": {
"specialist-subject": {
"type": "string",
"description": "Subject that this teacher specialises in."
}
}
}
}
}
Then we have the following instances, example1-valid.json
:
{
"student": {
"name": "Susan Smith",
"date-of-birth": "2010-01-01",
"graduation-year": "2028"
}
}
And example2-invalid.json
:
{
"teacher": {
"name": "Joseph Khan",
"date-of-birth": "1990-08-08",
"graduation-year": "2020"
}
}
When we try to validate these, we want example1 to pass and example2 to fail, because it’s introducing an element that shouldn’t be part of the teacher definition.
But they both pass:
$ jsonschema -i example1-valid.json simple-schema.json
$ jsonschema -i example2-invalid.json simple-schema.json
$
If we add additionalProperties="false"
to the definitions of student and teacher, they both fail:
$ jsonschema -i example1-valid.json simple-schema-no-additional-preferences.json
{'name': 'Susan Smith', 'date-of-birth': '2010-01-01', 'graduation-year': '2028'}: Additional properties are not allowed ('name', 'date-of-birth' were unexpected)
$ jsonschema -i example2-invalid.json simple-schema-no-additional-preferences.json
{'name': 'Joseph Khan', 'date-of-birth': '1990-08-08', 'graduation-year': '2020'}: Additional properties are not allowed ('graduation-year', 'date-of-birth', 'name' were unexpected)
$
The only way that we can get the functionality that we want is if we “unroll” the “inherited” objects and repeat the definitions of date-of-birth and name in both student and teacher. Then we can declare additionalProperties="false"
and it works:
{
"$id": "simple-schema-unrolled.json#",
"$schema": "http://json-schema.org/draft-06/schema#",
"description": "Really simple schema with unrolled inherited properties",
"additionalProperties": false,
"type": "object",
"properties": {
"student": {
"$ref": "#/definitions/student"
},
"teacher": {
"$ref": "#/definitions/teacher"
}
},
"definitions": {
"student": {
"type": "object",
"additionalProperties": false,
"properties": {
"date-of-birth": {
"type": "string",
"description": "Date of the person's birth."
},
"name": {
"type": "string",
"description": "The person's name."
},
"graduation-year": {
"type": "string",
"description": "Year in which the student is expected to graduate."
}
}
},
"teacher": {
"additionalProperties": false,
"type": "object",
"properties": {
"date-of-birth": {
"type": "string",
"description": "Date of the person's birth."
},
"name": {
"type": "string",
"description": "The person's name."
},
"specialist-subject": {
"type": "string",
"description": "Subject that this teacher specialises in."
}
}
}
}
}
Then our validation works properly:
$ jsonschema -i example1-valid.json simple-schema-unrolled.json
$ jsonschema -i example2-invalid.json simple-schema-unrolled.json
{'name': 'Joseph Khan', 'date-of-birth': '1990-08-08', 'graduation-year': '2020'}: Additional properties are not allowed ('graduation-year' was unexpected)
$
But this workaround requires huge amounts of duplication in our complex schema and makes it very difficult to maintain. We would be able to generate the “unrolled” schema automatically but we feel that JSON Schema should give us the ability to validate “inherited” properties out of the box.