JSON Schema - require all properties

Jsonschema

Jsonschema Problem Overview


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?

Jsonschema Solutions


Solution 1 - Jsonschema

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
}

Solution 2 - Jsonschema

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.

Solution 3 - Jsonschema

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
	}
}

Solution 4 - Jsonschema

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: []

Solution 5 - Jsonschema

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

Solution 6 - Jsonschema

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAdam MatanView Question on Stackoverflow
Solution 1 - JsonschemaSan JayView Answer on Stackoverflow
Solution 2 - JsonschemajruizarangurenView Answer on Stackoverflow
Solution 3 - JsonschemaDallasView Answer on Stackoverflow
Solution 4 - JsonschemaEliav LouskiView Answer on Stackoverflow
Solution 5 - JsonschemaEyal ShulmanView Answer on Stackoverflow
Solution 6 - Jsonschemadenisb411View Answer on Stackoverflow