Validate objects in JSON array with Ajv - jsonschema

I am trying to validate if the "actions" array has specific objects. Each object can have their own properties, depending on the type property. The actions array can have multiple occurences from these type of objects, not just one.
My JSON:
{
"actions": [
{
"type": "X",
"a": 1,
"b": 2,
"c": 3
},
{
"type": "Y",
"d": 1,
"e": 2,
"f": 3
}
]
}
In this example the object that has type = X has required properties as a, b, c. The object type Y in the array has required properties d, e, f.
I am trying to validate this with the switch keyword:
{
"type": "object",
"required": [
"actions"
],
"properties": {
"actions": {
"type": "array",
"items": {
"switch": [
{
"if": {
"properties": {
"type": {
"pattern": "^X$"
}
}
},
"then": {
"required": [
"a",
"b",
"c"
]
}
},
{
"if": {
"properties": {
"type": {
"pattern": "^Y$"
}
}
},
"then": {
"required": [
"d",
"e",
"f"
]
}
}
]
}
},
}
}
But still I could not figure out how to use the switch keyword to validate objects of arrays where each object type is specified by a property in the object so the object type can be identified with a property.

Your schema looks almost ok, you may need one last {then: false} subschema inside "switch", as without it if the "type" property is neither 'X' nor 'Y' the validation will succeed.
Also, instead of using {pattern: '^X$'} you can use {enum: ['X']} or even {constant: 'X'} (in draft-06 it is "const"); instead of using switch you can use "if/then/else" (from ajv-keywords, it is likely to be included in draft-07) or even "select", that may be more suitable for such scenario.
Maybe you can clarify the last question, I don't think I understand.

Related

Conditionally Required Attribute in JSON Schema (Draft-07) [duplicate]

In jsonSchema you can indicate whether defined fields are mandatory or not using the "required" attribute:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"header": {
"type": "object",
"properties": {
"messageName": {
"type": "string"
},
"messageVersion": {
"type": "string"
}
},
"required": [
"messageName",
"messageVersion"
]
}
},
"required": [
"header"
]
}
In certain cases, I would like the messageVersion field not to be mandatory. Is there any way to make the mandatory-ness of the this field conditional?
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"]
}
}
Found the solution to this.
Using allOf works for me.
Was just using a diff dependency altogether.
The right one to use for draft07 schema is :
<dependency>
<groupId>com.github.erosb</groupId>
<artifactId>everit-json-schema-jdk6</artifactId>
<version>1.9.2</version>
</dependency>

Should dependentSchemas property need to be added in additionalProperties

I am trying to write a schema, where if property A is present then property B or C should also per present using dependencyschema construct, and in my schema, I have turned off any additionalProperties.
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$id": "PropertiesSchema.json",
"type": "object",
"properties": {
"A": { "type": "boolean" }
},
"additionalProperties": false,
"dependentSchemas": {
"A": {
"anyOf": [
{ "required": [ "B" ] },
{ "required": [ "C" ] }
],
"properties": {
"B": { "type": "boolean" },
"C": { "type": "boolean" }
}
}
}
}
but it is currently failing for below input becuase of additionalProperties construct
{ "A": true, "B": false } or { "A": true, "C": false }
So is it possible that dependentSchemas properties are allowed when additionalProperties is turned off?
Thus valid input should be
{ "A": true, "B": false } or { "A": true, "C": false }
but for any other property say D, it should fail-
{ "A": true, "D": false }
additionalProperties can only take into account properties (and patternProperties) at the same schema object level.
If you change this to unevaluatedProperties you will find it working.
unevaluatedProperties can "see through" applicator keywords, such as dependentSchemas. It must wait for other keywords to be resolved first, and is resolved last.

JSON Schema: Conditionally require property depending on several properties' values

I want to validate a JSON file using JSON schema, several "properties" should be required depending on what values some other properties have.
Example
Having properties "A", "B", "C", and "D"
if "A" has value "foo", C is required
if "B" has value "foo", D is required
if both "A" and "B" each have value "foo", both C and D are required
else, nothing is required
I have seen a very helpful answer here: https://stackoverflow.com/a/38781027/5201771 --> there, the author addresses how to solve this problem for the case of a single property (e.g., only "A" has value "foo", so require "C").
However, I currently don't see how to extend that answer to my case, where several properties determine the outcome.
Example Files
for illustration, I supply some files that should pass or fail the validation:
should pass:
{
"A": "bar"
"B": "baz"
}
{
"A": "foo"
"C": "some value"
}
{
"A": "bar"
"B": "foo"
"D": "some value"
}
should fail:
{
"A": "foo"
"B": "foo"
"D": "some value"
}
You can combine conditionals a number of ways, but combining them with allOf is usually the best way.
{
"type": "object",
"properties": {
"A": {},
"B": {},
"C": {},
"D": {}
},
"allOf": [
{ "$ref": "#/definitions/if-A-then-C-is-required" },
{ "$ref": "#/definitions/if-B-then-D-is-required" }
],
"definitions": {
"if-A-then-C-is-required": {
"if": {
"type": "object",
"properties": {
"A": { "const": "foo" }
},
"required": ["A"]
},
"then": { "required": ["C"] }
},
"if-B-then-D-is-required": {
"if": {
"type": "object",
"properties": {
"B": { "const": "foo" }
},
"required": ["B"]
},
"then": { "required": ["D"] }
}
}
}

JSON schema: Use field value as required field name

I need to define a JSON schema for a JSON in which a field/key is called as the value of a previous field. Examples:
{
"key1": "SOME_VALUE",
"SOME_VALUE": "..."
}
{
"key1": "ANOTHER_VALUE",
"ANOTHER_VALUE": "..."
}
Moreover, the second field should be among the required ones.
I have been looking around but I am not sure JSON schema offers such feature. Maybe some advanced semantics check?
Thanks for the help
The only way you could do this is if you knew the values in advance, but it looks like this is not possible for you. This would need to be in your business logic validation as opposed to your format validation.
So, thanks to Relequestual suggestions, I managed to get to a solution.
Constraint: possible values of "key1" need to be finite and known in advance
Suppose we need a JSON schema for validating a JSON that:
Requires the string properties "required_simple_property1" and "required_simple_property2".
Requires the property "key1" as an enum with 3 possible values ["value1", "value2", "value3"].
Requires a third property, whose key must be the value taken by key1.
This can be accomplished with a schema like:
"oneOf": [
{
"required": [
"required_simple_property1",
"required_simple_property2",
"value1"
],
"properties": {
"key1": {
"type": "string",
"const": "value1"
}
}
},
{
"required": [
"required_simple_property1",
"required_simple_property2",
"value2"
],
"properties": {
"key1": {
"type": "string",
"const": "value2"
}
}
},
{
"required": [
"required_simple_property1",
"required_simple_property2",
"value3"
],
"properties": {
"key1": {
"type": "string",
"const": "value3"
}
}
}
],
"properties": {
"required_simple_property1": {
"type": "string"
},
"required_simple_property2": {
"type": "string"
},
"value1": {
... (anything)
},
"value2": {
... (anything)
},
"value3": {
... (anything)
},
}

jsonSchema attribute conditionally required

In jsonSchema you can indicate whether defined fields are mandatory or not using the "required" attribute:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"header": {
"type": "object",
"properties": {
"messageName": {
"type": "string"
},
"messageVersion": {
"type": "string"
}
},
"required": [
"messageName",
"messageVersion"
]
}
},
"required": [
"header"
]
}
In certain cases, I would like the messageVersion field not to be mandatory. Is there any way to make the mandatory-ness of the this field conditional?
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"]
}
}
Found the solution to this.
Using allOf works for me.
Was just using a diff dependency altogether.
The right one to use for draft07 schema is :
<dependency>
<groupId>com.github.erosb</groupId>
<artifactId>everit-json-schema-jdk6</artifactId>
<version>1.9.2</version>
</dependency>