60

The required field in JSON Schema

JSON Schema features the properties, required and additionalProperties fields. For example,

{
    "type": "object",
    "properties": {
        "elephant": {"type": "string"},
        "giraffe": {"type": "string"},
        "polarBear": {"type": "string"}
    },
    "required": [
        "elephant",
        "giraffe",
        "polarBear"
    ],
    "additionalProperties": false
}

Will validate JSON objects like:

{
    "elephant": "Johnny",
    "giraffe": "Jimmy",
    "polarBear": "George"
}

But will fail if the list of properties is not exactly elephant, giraffe, polarBear.

The problem

I often copy-paste the list of properties to the list of required, and suffer from annoying bugs when the lists don't match due to typos and other silly errors.

Is there a shorter way to denote that all properties are required, without explicitly naming them?

Adam Matan
  • 128,757
  • 147
  • 397
  • 562
  • 12
    It would be nice if the JSON Schema spec supported `"required": true`, where the boolean replaces the usual array. – rcrogers Sep 14 '17 at 20:49

7 Answers7

63

You can just use the "minProperties" property instead of explicity naming all the fields.

{
    "type": "object",
    "properties": {
        "elephant": {"type": "string"},
        "giraffe": {"type": "string"},
        "polarBear": {"type": "string"}
    },
    "additionalProperties": false,
    "minProperties": 3
}
San Jay
  • 784
  • 6
  • 7
  • Best approach so far. – Adam Matan Nov 01 '16 at 22:15
  • 18
    Thanks, this works great. One thing to keep in mind though is that it depends on the specification of `"additionalProperties": false` If additionalProperties is true by specification or by default (if unspecified), then the minProperties constraint can be satisfied by an additional property that is _not_ one of the specified ones in your schema object. – yokeho Jul 20 '17 at 17:51
  • 8
    The error message is less useful this way. You know you're missing a property, but have to figure out which one. Still, beats copying all the properties each time. – Memetic Sep 25 '17 at 22:42
  • Great solution. I'd like to use this, though I'm finding it a challenge since my #properties in a given object is dependent on a "oneOff" selection. As such, minProperties should ideally switch between two integers, depending on the user's selection in another property. Any ideas on if/how this can be achieved? – Martin Jan 29 '19 at 18:59
  • This doesn't work very well when your object is an extension of another object using `allOf`. It requires the parent object to have all properties required in addition to the child object. It also requires going through each child object and updating property counts if the parent object changes. – Joseph May 08 '20 at 18:06
  • This is a workaround that indirectly solves the problem. It may be a helpful answer but should not be the accepted one in my opinion. – Rafe Oct 16 '21 at 15:29
  • AS mentioned, this ONLY works with `additionalProperties` set as false, NOT if they are true. – Matthias Sep 07 '22 at 11:26
9

I doubt there exists a way to specify required properties other than explicitly name them in required array.

But if you encounter this issue very often I would suggest you to write a small script that post-process your json-schema and add automatically the required array for all defined objects.

The script just need to traverse the json-schema tree, and at each level, if a "properties" keyword is found, add a "required" keyword with all defined keys contained in properties at the same level.

Let the machines do the bore stuff.

jruizaranguren
  • 12,679
  • 7
  • 55
  • 73
1

I do this in code with a one-liner, for instance, if I want to use required for insert in a DB, but only want to validate against the schema when performing an update.

prepareSchema(action) {
    const actionSchema = R.clone(schema)
    switch (action) {
        case 'insert':
            actionSchema.$id = `/${schema.$id}-Insert`
            actionSchema.required = Object.keys(schema.properties)
            return actionSchema
        default:
            return schema
    }
}
Dallas
  • 888
  • 3
  • 10
  • 24
1

if you using the library jsonschema in python use custom validators:

first create custom validator:

# Custom validator for requiring all properties listed in the instance to be in the 'required' list of the instance
def allRequired(validator, allRequired, instance, schema):
    if not validator.is_type(instance, "object"):
        return
    if allRequired and "required" in instance:
        # requiring all properties to 'required'
        instanceRequired = instance["required"]
        instanceProperties = list(instance["properties"].keys())
        for property in instanceProperties:
            if property not in instanceRequired:
                yield ValidationError("%r should be required but only the following are required: %r" % (property, instanceRequired))
        for property in instanceRequired:
            if property not in instanceProperties:
                yield ValidationError("%r should be in properties but only the following are properties: %r" % (property, instanceProperties))

then extend an exsitsing validator:

all_validators = dict(Draft4Validator.VALIDATORS)
all_validators['allRequired'] = allRequired

customValidator = jsonschema.validators.extend(
    validator=Draft4Validator,
    validators=all_validators
)

now test:

schema =  {"allRequired": True}
instance = {"properties": {"name": {"type": "string"}}, "required": []}
v = customValidator(schema)
errors = validateInstance(v, instance)

you will get the error: 'name' should be required but only the following are required: []

Eliav Louski
  • 3,593
  • 2
  • 28
  • 52
  • Nice idea. If I wasn't trying to stay language agnostic with the implementation I'd go with this answer. I suppose this could be pushed in to the source. – Rafe Oct 16 '21 at 15:32
1

You can use the function below:

export function addRequiredAttributeRecursive(schema) {
  if (schema.type === 'object') {
    schema.required = [];
    Object.keys(schema.properties).forEach((key) => {
      schema.required.push(key);
      if (schema.properties[key].type === 'object') {
        schema.properties[key] = addRequiredAttributeRecursive(
          schema.properties[key],
        );
      } else if (schema.properties[key].type === 'array') {
        schema.properties[key].items = addRequiredAttributeRecursive(
          schema.properties[key].items,
        );
      }
    });
  } else if (schema.type === 'array') {
    if (schema.items.type === 'object') {
      schema.items = addRequiredAttributeRecursive(schema.items);
    }
  }

  return schema;
}

It recursively write the required attribute for every property on all objects from the schema you have.

denisb411
  • 581
  • 1
  • 7
  • 25
0

As suggested by others, here's such post-processing python code:

def schema_to_strict(schema):
    if schema['type'] not in ['object', 'array']:
        return schema

    if schema['type'] == 'array':
        schema['items'] = schema_to_strict(schema['items'])
        return schema

    for k, v in schema['properties'].items():
        schema['properties'][k] = schema_to_strict(v)

    schema['required'] = list(schema['properties'].keys())
    schema['additionalProperties'] = False
    return schema
Eyal Shulman
  • 699
  • 5
  • 15
0

If you are using Javascript, you can use property getter.

{
    "type": "object",
    "properties": {
        "elephant": {"type": "string"},
        "giraffe": {"type": "string"},
        "polarBear": {"type": "string"}
    },
    get required() { return Object.keys(this.properties) },
    "additionalProperties": false
}
Sang
  • 4,049
  • 3
  • 37
  • 47