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

✨ Proposal: Add a Global defaultAdditionalProperties Toggle at the Root Level #1587

Open
dani2819 opened this issue Feb 24, 2025 · 2 comments
Labels
proposal Initial discussion of a new idea. A project will be created once a proposal document is created.

Comments

@dani2819
Copy link

Describe the inspiration for your proposal

Currently, JSON Schema requires additionalProperties: false to be set explicitly on every object where I want to disallow extra properties. This gets repetitive when I want all objects in my schema (root and nested) to enforce this rule consistently. For example, I have a schema like this:

{
  'type': 'object',
  'additionalProperties': false,
  'properties': {
    'name': { 'type': 'string' },
    'details': {
      'type': 'object',
      'additionalProperties': false,
      'properties': {
        'age': { 'type': 'integer' },
        'city': { 'type': 'string' }
      }
    }
  }
}

I have to repeat additionalProperties: false for both the root object and the details object. In a larger schema with many nested objects, this repetition grows tedious and increases the chance of forgetting it somewhere. There’s no way to set this once globally and have it cascade to all objects, so I’m stuck either duplicating it or using $ref to a shared definition, which still requires manual application each time. A global setting would make this cleaner and align with the DRY.

Describe the proposal

Add a new root-level keyword, like defaultAdditionalProperties, that sets the default value for additionalProperties across all objects in the schema unless overridden. For example:

{
  '$schema': 'http://json-schema.org/draft-07/schema#',
  'defaultAdditionalProperties': false,
  'type': 'object',
  'properties': {
    'name': { 'type': 'string' },
    'details': {
      'type': 'object',
      'properties': {
        'age': { 'type': 'integer' }
      }
    }
  }
}

Benefits

  • Reduces redundancy and simplifies schemas.
  • Makes strict schemas easier to write and maintain.
  • Still allows flexibility with local overrides.
  • Aligns with other global defaults in JSON Schema (e.g., default for values).

Describe alternatives you've considered

  • Using $defs and $ref works but still requires explicit references per object, not a true global solution.
  • Preprocessing schemas externally is an option, but it’s outside the spec and not portable.

Additional context

No response

@dani2819 dani2819 added the proposal Initial discussion of a new idea. A project will be created once a proposal document is created. label Feb 24, 2025
@gregsdennis
Copy link
Member

gregsdennis commented Feb 24, 2025

I see where you're coming from, but this violates a core principle of how JSON Schema works. Specifically, a schema can only "see" that which is inside of it; it can't see anything in the JSON structure above it. So any subschema would be unaware that your new defaultAdditionalProperties keyword exists in the root.

Let's work through your example:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "defaultAdditionalProperties": false,
  "type": "object",
  "properties": {
    "name": { "type": "string" },
    "details": {
      "type": "object",
      "properties": {
        "age": { "type": "integer" }
      }
    }
  }
}

Your intent is for the subschema at /properties/details to also have a restriction where there are no additional properties. However, the only data that this subschema has to work from is

{
  "type": "object",
  "properties": {
    "age": { "type": "integer" }
  }
}

It has no way of knowing there is a keyword at the root defining more constraints.


What happens with a $ref? Currently $ref is defined so that your example is equivalent to

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://example.test/root",
  "defaultAdditionalProperties": false,
  "type": "object",
  "properties": {
    "name": { "type": "string" },
    "details": { "$ref": "./details" }
  }
}

{
  "$id": "https://example.test/details",
  "type": "object",
  "properties": {
    "age": { "type": "integer" }
  }
}

The schema https://example.test/details is expected to operate completely independently. Does the defaultAdditionalProperties still apply to it? What if I reference it from two different places, one of which has defaultAdditionalProperties and the other doesn't?

{
  "$id": "https://example.test/foo",
  "type": "object",
  "properties": {
    "bar": { "$ref": "./bar" },
    "baz": { "$ref": "./baz" }
  }
}

{
  "$id": "https://example.test/bar",
  "type": "object",
  "defaultAdditionalProperties": false,
  "properties": {
    "person": { "$ref": "./person" }
  }
}

{
  "$id": "https://example.test/baz",
  "type": "object",
  "properties": {
    "person": { "$ref": "./person" }
  }
}

{
  "$id": "https://example.test/person",
  "type": "object",
  "properties": {
    "name": { "type": "string" },
    "age": { "type": "integer" }
  }
}

So evaluating data looks like this:

{
  "foo": {
    "name": "Steve",
    "age": 13,
    "location": "Auckland" // invalid
  },
  "bar": {
    "name": "Steve",
    "age": 13,
    "location": "Auckland" // valid
  }
}

Both /foo and /bar are defined to meet the requirements of https://example.test/person, but one is valid and the other isn't.

@jhsenjaliya
Copy link

jhsenjaliya commented Mar 26, 2025

I agree, I have seen the usage of this at every object level than global.
Though having a way to do this globally can be nice to have configuration, and may be should be implemented at library level ( ex: similar to config like includeJsr303Annotations in jsonSchema2Pojo )

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
proposal Initial discussion of a new idea. A project will be created once a proposal document is created.
Projects
None yet
Development

No branches or pull requests

3 participants