Depending on your situation, there are a few different approaches. I can think of four different ways to conditionally require a field.
Dependencies
The dependentSchemas keyword is a conditional way to apply a schema. Foreach property in dependentSchemas, if the property is present in the JSON being validated, then the schema associated with that key must also be valid. If the "foo" property is present, then the "bar" property is required
{
"type": "object",
"properties": {
"foo": { "type": "string" },
"bar": { "type": "string" }
},
"dependentSchemas": {
"foo": { "required": ["bar"] }
}
}
If all the dependent schema needs is the required keyword, you can use the dependentRequired keyword as a shorthand. The following has the same effect as the previous example.
{
"type": "object",
"properties": {
"foo": { "type": "string" },
"bar": { "type": "string" }
},
"dependentRequired": {
"foo": ["bar"]
}
}
NOTE: In draft-07 and below these were one keyword called dependencies. If the value is a schema it behaved like dependentSchemas. If the value is an array, it behaved like dependentRequired.
Implication
If your condition depends on the value of a field, you can use a boolean logic concept called implication. "A implies B" effectively means, if A is true then B must also be true. Implication can also be expressed as "!A or B". Either the "foo" property does not equal "bar", or the "bar" property is required. Or, in other words: If the "foo" property equals "bar", Then the "bar" property is required
{
"type": "object",
"properties": {
"foo": { "type": "string" },
"bar": { "type": "string" }
},
"anyOf": [
{
"not": {
"properties": {
"foo": { "const": "bar" }
},
"required": ["foo"]
}
},
{ "required": ["bar"] }
]
}
If "foo" is not equal to "bar", #/anyOf/0 matches and validation succeeds. If "foo" equals "bar", #/anyOf/0 fails and #/anyOf/1 must be valid for the anyOf validation to be successful.
NOTE: The if/then keywords have the same behavior, but are easier to read and maintain. It's recommended to only use this approach if you are using an older version of JSON Schema that doesn't support if/then.
Enum
If your conditional is based on an enum, it's a little more straight forward. "foo" can be "bar" or "baz". If "foo" equals "bar", then "bar" is required. If "foo" equals "baz", then "baz" is required.
{
"type": "object",
"properties": {
"foo": { "enum": ["bar", "baz"] },
"bar": { "type": "string" },
"baz": { "type": "string" }
},
"anyOf": [
{
"properties": {
"foo": { "const": "bar" }
},
"required": ["bar"]
},
{
"properties": {
"foo": { "const": "baz" }
},
"required": ["baz"]
}
]
}
NOTE: This approach is not recommended because it can produce confusing error messages. The if/then keywords are generally a better approach.
If-Then-Else
The if, then and else keywords are shorthand for the implication pattern described above. These keywords were added in draft-07. If the "foo" property equals "bar", Then the "bar" property is required
{
"type": "object",
"properties": {
"foo": { "type": "string" },
"bar": { "type": "string" }
},
"if": {
"properties": {
"foo": { "const": "bar" }
},
"required": ["foo"]
},
"then": { "required": ["bar"] }
}
EDIT 12/23/2017: Implication section updated and If-Then-Else section added.
EDIT 06/04/2018: Bugfix for If-Then-Else and update singleton enums to use const.
EDIT 07/06/2022: Update Dependencies section to use the new dependentSchemas/dependentRequired keywords instead of dependencies.
Json Schema validation using If-Else - "conditionally choosing required element" not working - General - JSON Forms community
Field required based on condition
validation - How do I use the if-then-else condition in json schema? - Stack Overflow
Need help implementing conditional requirements in JSON schema?
Videos
Depending on your situation, there are a few different approaches. I can think of four different ways to conditionally require a field.
Dependencies
The dependentSchemas keyword is a conditional way to apply a schema. Foreach property in dependentSchemas, if the property is present in the JSON being validated, then the schema associated with that key must also be valid. If the "foo" property is present, then the "bar" property is required
{
"type": "object",
"properties": {
"foo": { "type": "string" },
"bar": { "type": "string" }
},
"dependentSchemas": {
"foo": { "required": ["bar"] }
}
}
If all the dependent schema needs is the required keyword, you can use the dependentRequired keyword as a shorthand. The following has the same effect as the previous example.
{
"type": "object",
"properties": {
"foo": { "type": "string" },
"bar": { "type": "string" }
},
"dependentRequired": {
"foo": ["bar"]
}
}
NOTE: In draft-07 and below these were one keyword called dependencies. If the value is a schema it behaved like dependentSchemas. If the value is an array, it behaved like dependentRequired.
Implication
If your condition depends on the value of a field, you can use a boolean logic concept called implication. "A implies B" effectively means, if A is true then B must also be true. Implication can also be expressed as "!A or B". Either the "foo" property does not equal "bar", or the "bar" property is required. Or, in other words: If the "foo" property equals "bar", Then the "bar" property is required
{
"type": "object",
"properties": {
"foo": { "type": "string" },
"bar": { "type": "string" }
},
"anyOf": [
{
"not": {
"properties": {
"foo": { "const": "bar" }
},
"required": ["foo"]
}
},
{ "required": ["bar"] }
]
}
If "foo" is not equal to "bar", #/anyOf/0 matches and validation succeeds. If "foo" equals "bar", #/anyOf/0 fails and #/anyOf/1 must be valid for the anyOf validation to be successful.
NOTE: The if/then keywords have the same behavior, but are easier to read and maintain. It's recommended to only use this approach if you are using an older version of JSON Schema that doesn't support if/then.
Enum
If your conditional is based on an enum, it's a little more straight forward. "foo" can be "bar" or "baz". If "foo" equals "bar", then "bar" is required. If "foo" equals "baz", then "baz" is required.
{
"type": "object",
"properties": {
"foo": { "enum": ["bar", "baz"] },
"bar": { "type": "string" },
"baz": { "type": "string" }
},
"anyOf": [
{
"properties": {
"foo": { "const": "bar" }
},
"required": ["bar"]
},
{
"properties": {
"foo": { "const": "baz" }
},
"required": ["baz"]
}
]
}
NOTE: This approach is not recommended because it can produce confusing error messages. The if/then keywords are generally a better approach.
If-Then-Else
The if, then and else keywords are shorthand for the implication pattern described above. These keywords were added in draft-07. If the "foo" property equals "bar", Then the "bar" property is required
{
"type": "object",
"properties": {
"foo": { "type": "string" },
"bar": { "type": "string" }
},
"if": {
"properties": {
"foo": { "const": "bar" }
},
"required": ["foo"]
},
"then": { "required": ["bar"] }
}
EDIT 12/23/2017: Implication section updated and If-Then-Else section added.
EDIT 06/04/2018: Bugfix for If-Then-Else and update singleton enums to use const.
EDIT 07/06/2022: Update Dependencies section to use the new dependentSchemas/dependentRequired keywords instead of dependencies.
As of 2022, dependencies has been deprecated, and split into dependentRequired (see e.g. this example) and dependentSchemas (see e.g. this example). Just using dependentRequired solves the issue:
{
"type": "object",
"properties": {
"foo": { "type": "string" },
"bar": { "type": "string" }
},
"dependentRequired": {
"foo": ["bar"]
}
}
The if keyword means that, if the result of the value schema passes validation, apply the then schema, otherwise apply the else schema.
Your schema didn't work because you needed to require "foo" in your if schema, otherwise an empty JSON instance would pass validation of the if schema, and therefore apply the then schema, which requires "bar".
Second, you want "propertyNames":false, which prevents having any keys in the schema, unlike if you were to set "else": false which would make anything always fail validation.
{
"type": "object",
"properties": {
"foo": {
"type": "string"
},
"bar": {
"type": "string"
}
},
"if": {
"properties": {
"foo": {
"enum": [
"bar"
]
}
},
"required": [
"foo"
]
},
"then": {
"required": [
"bar"
]
},
"else": false
}
Can't you just use the "else" property ?
{
"type": "object",
"properties": {
"foo": { "type": "string" },
"bar": { "type": "string" }
},
"if": {
"properties": {
"foo": {
"enum": ["bar"]
}
}
},
"then": {
"required": ["bar"]
},
"else": {
"required": []
}
}
I'm using AWS's API Gateway to expose an HTTP API. It allows me to use JSON schema's to map and validate the input body, and I'd like to implement conditional requirements, however, I'm not sure how. Also, I'm not sure whether I should be doing this, or creating a second JSON schema.
In this example, I have an API that can take in two completely different things, apples & oranges if you will. I have the below schema:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Apples2Oranges",
"type": "object",
"properties": {
"apples": {
"type": "array",
"items": {
"type": "string"
}
},
"oranges": {
"type": "array",
"items": {
"type": "string"
}
},
"apple-property": { "type": "string" }
}What I want to do is require that the body contains an array for either apples or oranges, but not both! Additionally, if the user supplies a value for apples, they are also required to submit a value for apples-property, they shouldn't be able to submit a value here if they chose oranges...
Is this sort of conditional logic something I should do with JSON schemas? Now that I'm writing this out, it sounds like this would be much easier if I simply had two separate models...
There are several ways to achieve required effect even not using JSON Schema draft-07 if-then-else.
logical operator and implication (draft-04 and above)
A logical implication here: if "medium" present then "bulky" is required can be translated to "medium" not present OR "bulky" is "required" (the latter implicates "medium" is present) which can be further elaborated to "medium" not required OR "bulky" is "required" (since if "medium" is present, it will satisfy condition of being required). See below schema:
"properties": {
"smaller": {"type": "number"},
"larger": { "type": "number" },
"medium":{"type":"string"},
"bulky":{"type":"string"}
},
"required":["smaller","larger"],
"anyOf" : [
{
"not" : { "required" : ["medium"] }
},
{
"required" : ["bulky"]
}
],
"additionalProperties" : false
Check here for reference:
JSON schema - valid if object does *not* contain a particular property
http://json-schema.org/latest/json-schema-validation.html#rfc.section.6.7
"anyOf" - logical OR, "oneOf" - XOR, "allOf" - AND, "not" - negation, yet pay attention to spec:
An instance is valid against this keyword if it fails to validate successfully against the schema defined by this keyword.
draft-06 - dependencies + propertyNames
Most obvious. I am not sure if you excluded this one in your question, so putting here just in case. Please note, that instead of "additionalProperties", if you wan't simply to limit valid keys, "propertyNames" could be used (and is actually what it was added for).
"properties": {
"smaller": {"type": "number"},
"larger": { "type": "number" },
"medium":{"type":"string"},
"bulky":{"type":"string"}
},
"required":["smaller","larger"],
"dependencies" : {
"medium" : ["bulky"]
},
"propertyNames" : {
"enum" : [
"smaller",
"larger",
"medium",
"bulky"
]
}
Check here for reference: http://json-schema.org/latest/json-schema-validation.html#rfc.section.6.5.7
Update
After clarification in comment:
for draft-6 - here "not require" means that if "medium" dont exist then bulky " must not be present"
"must not" means preventing bulky being present.
I will rephrase your condition:
1. if "medium" exists "bulky" must be present -> both keys must be present at the same time
2. if "medium" does not exist "bulky" must not be present as well -> both keys must not be present at the same time
Can "bulky" exist and "medium" does not exist?
No. See 2. And vice versa (see 1.). Boolean equality (complementary to logical XOR).
Thus if "bulky" exists - it means "medium" must be always there... It implies that both are required or both must not be required (or even allowed).
Since it's draft-06, you can use also "propertyNames" for defining allowed property names (kind of shortcut to this logic).
logical operator and implication (draft-06 and above)
The proper logical operation translated to JSOn Schema would look like:
"oneOf" : [
{ "required" : ["medium","bulky"] }, <== this schema is satisfied if both keys appear in validated instance
{
"allOf" : [ <== !medium ^ !bulky - due to how "not" works in schema context
{"not" : { "required" : ["medium"] } },
{"not" : { "required" : ["bulky"] } },
]
}
]
An XOR - EITHER (both required) OR (medium not required AND bulky not required).
Please note I am not doing "not" : { "required" : ["medium","bulky"] } as when just one of those keys is present, "required" schema would fail which would mean "not" would return successfull validation result. One needs to rephrase it using de Morgans laws:
"oneOf" : [
{ "required" : ["medium","bulky"] },
{
"not" : { <=== !medium ^ !bulky = !(medium v bulky)
"anyOf" : [
{ "required" : ["medium"] },
{ "required" : ["bulky"] },
]
}
}
]
However using "propertyNames" will also do the trick. See following schema:
{
"$schema": "http://json-schema.org/draft-06/schema#",
"properties": {
"smaller": {"type": "number"},
"larger": { "type": "number" },
"medium":{"type":"string"},
"bulky":{"type":"string"}
},
"required":["smaller","larger"],
"anyOf" : [
{
"required" : ["medium","bulky"]
},
{
"propertyNames" : {
"enum" : [
"smaller",
"larger"
]
},
}
],
"examples" : [
{
"smaller" : 1,
"larger" : 2,
},
{
"smaller" : 1,
"larger" : 2,
"bulky" : "test",
"medium" : ""
},
{
"smaller" : 1,
"larger" : 2,
"medium" : ""
},
{
"smaller" : 1,
"larger" : 2,
"bulky" : "test",
},
]
}
Does it answer your question?
JSON Schema Draft-07 has included these new keywords if, then and else which allow you to have conditional schemas.
In this example:
- Only the
fooproperty is required - However if
foois set to"bar"then thebarproperty also becomes required
var ajv = new Ajv({
allErrors: true
});
var schema = {
"properties": {
"foo": {
"type": "string"
},
"bar": {
"type": "string"
},
},
"required": ["foo"],
"if": {
"properties": {
"foo": {
"enum": ["bar"]
}
}
},
"then": {
"required": ["bar"]
}
}
var validate = ajv.compile(schema);
test({
"foo": "bar",
"bar": "baz"
}); // VALID
test({
"foo": "xyz"
}); // VALID
test({
"foo": "bar",
}); // NOT VALID
function test(data) {
var valid = validate(data);
if (valid) console.log('VALID', data);
else console.log('NOT VALID', data);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/ajv/6.5.5/ajv.min.js"></script>
Hopefully this makes sense and you can adapt your code accordingly.
PS: in your schema you have the require property which I'm not sure is a valid JSON Schema keyword. You probably meant required instead.