Preventing dependent property validation when the parent property does not exist - jsonschema

I am new to JSON schemas. I have a property (property1) that is dependent on another property (property2), which in turn is dependent on a third property (property3). I am trying to figure out how to prevent the schema from validating property1 if property2 doesn't exist. I am using the Python jsonschema module for validating.
I have a simple schema with three properties: species, otherDescription, and otherDescriptionDetail. The rules I'm trying to enforce are:
1) if species = "Human", otherDescription is required.
2) if species = "Human" and otherDescription != "None", otherDescriptionDetail is required.
3) if species != "Human", neither of the other two fields is required.
My test JSON correctly fails validation if species is "Human" and otherDescription doesn't exist, but it also reports that otherDescriptionDetail is a required property even though at this point it shouldn't be because there is no otherDescription value to compare it against. Is it possible to implement this logic with a JSON schema?
This is my schema:
"$schema": "http://json-schema.org/draft-07/schema#",
"$id":"http://example.com/test_schema.json",
"title": "annotations",
"description": "Validates file annotations",
"type": "object",
"properties": {
"species": {
"description": "Type of species",
"anyOf": [
{
"const": "Human",
"description": "Homo sapiens"
},
{
"const": "Neanderthal",
"description": "Cave man"
}
]
},
"otherDescription": {
"type": "string"
},
"otherDescriptionDetail": {
"type": "string"
}
},
"required": [
"species"
],
"allOf": [
{
"if": {
"properties": {
"species": {
"const": "Human"
}
}
},
"then": {
"required": ["otherDescription"]
}
},
{
"if": {
"allOf": [
{
"properties": {
"species": {
"const": "Human"
},
"otherDescription": {
"not": {"const": "None"}
}
}
}
]
},
"then": {
"required": ["otherDescriptionDetail"]
}
}
]
}
My test JSON is:
{
"species": "Human"
}
The output that I want:
0: 'otherDescription' is a required property
The output that I am getting:
0: 'otherDescription' is a required property
1: 'otherDescriptionDetail' is a required property
Any help would be greatly appreciated.

You need to defined otherDescription as a required property insilde allOf. Otherwise allOf block will pass even if otherDescription not available.
"if": {
"allOf": [
{
"properties": {
"species": {
"const": "Human"
},
"otherDescription": {
"not": {"const": "None"}
}
},
"required": ["otherDescription"]
}
]
},
"then": {
"required": ["otherDescriptionDetail"]
}

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>

How to ref JSON schema definition of type array with anyOf definition in other files

I have a jsonschema definition definition-1.json like below. I have a property called inputs of type array which refers anyOf definitions like
"inputs": {
"type": "array",
"items": {
"anyOf": [
{
"$ref": "#/definitions/stringParameter"
},
{
"$ref": "#/definitions/numberParameter"
},
{
"$ref": "#/definitions/booleanParameter"
},
{
"$ref": "#/definitions/jsonParameter"
},
{
"$ref": "#/definitions/fileParameter"
}
]
}
}
Then the parameter definitions defined like below in the same jsonschema definition-1.json
"definitions": {
"stringParameter": {
"type": "object",
"required": [
"name",
"description",
"datatype"
]
}
...
}
Now I want to reference this parameter definitions in my other schema dummy-1.json like below
{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "dummy-1.json",
"allOf": [
{
"type": "object",
"required": [
"definition_data"
],
"properties": {
"definition_data": {
"type": "object",
"required": [
"inputs"
],
"properties": {
"type": "array",
"inputs": {
"items": {
"allOf": [
{
"prop1": {
"$ref": "#/definitions/stringParameter"
}
},
{
"prop2": {
"$ref": "#/definitions/numberParameter"
}
}
]
}
}
}
}
}
},
{
"type": "object",
"properties": {
"definition_data": {
"$ref": "definition-1.json"
}
}
}
]
}
This doesn't looks like working. prop1 will validate successfully for any properties part of other parameter definition too , even though in dummy-1.json I explicitly referred #/definitions/stringParameter .
I can understand inputs in definition-1.json accepts anyOf all parameter definitions. But want to know how we can achieve 1:1 parameter definition mapping for dummy-1.json .
If my understanding is correct, you want to refer from dummy-1.json to subschemas defined in definitions-1.json. To do that, you have to specify the URI of definition-1.json and append the pointer of the subschema in it, like:
dummy-1.json:
{
"$ref": "uri-of-definitions-1.json#/definitions/stringParameter"
}
Note that the URI to be set is quite specific to the json schema library you are using. Usually an absolute https:// URL works, but it isn't always convenient to work with. Maybe a relative path can work for you as well (like ./definition-1.json#/definitions/stringParameter)

Json Schema template for valiadtion

I am new to defining JSON schema and validating json against the schema.
Here is a sample json for which I want to define a json schema template for validation:
{
 "version": "1.0",
 "config": {
   "globalConfig": {
      “ClientNames”: [
        “client1”, “client2”, “client3”
       ]
    },
“ClientConfigs”: [
{
“ClientName”: “client1”,
“property1”: “some value”,
“property2”: “some value”
},
{
“ClientName”: “client2”,
“property1”: “some value”,
“property2”: “some value”
},
{
“ClientName”: “client3”,
“property1”: “some value”,
“property2”: “some value”
}
]
}
From what I understand “ClientConfigs” would be an array of object (let’s say ClientConfig) which will contain clientName, property1 and property2. Here is what I think schema would like:
{
"$schema": "http://json-schema.org/draft-01/schema#",
"title": "ClientConfig",
"type": "object",
"description": "Some configuration",
"properties": {
"version": {
"type": "string"
},
"config": {
"$ref": "#/definitions/config"
}
},
"definitions": {
"config": {
"type": "object",
"properties": {
"globalConfig": {
"type": "object",
"description": "Global config for all clients",
"properties": {
"ClientNames": {
"type": "array",
"minItems": 1,
"items": {
"type": "string"
}
}
}
},
"ClientConfigs": {
"type": "array",
"description": "List of configs for different clients",
"minItems": 1,
"items": {
"$ref": "#/definitions/ClientConfig"
}
}
}
},
"ClientConfig": {
"type": "object",
"properties": {
"ClientName": {
"type": "string"
},
"property1": {
"type": "string"
},
"property2": {
"type": "string"
}
}
}
}
}
I want to validate 2 things with jsonschema:
ClientName in every element of ClientConfigs array is one of the values from “ClientNames” i.e. individual ClientConfig in “ClientConfigs” array should only contain client names defined in property “ClientNames”.
Every clientName present in “ClientNames” should be defined as an element in “ClientConfigs” array. To be more precise, ClientConfig is defined for every clientName present in “ClientNames” property.
Here is an example which is NOT valid according to my requirements:
{
 "version": "1.0",
 "config": {
   "globalConfig": {
      “ClientNames”: [
        “client1”, “client2”, “client3”
       ]
    },
“ClientConfigs”: [
{
“ClientName”: “client4”,
“property1”: “some value”,
“property2”: “some value”
}
]
}
It is invalid because:
It doesn’t define ClientConfig for client1, client2 and client3.
It defines ClientConfig for client4 which is not present in “ClientNames”.
Is it possible to do such validation using json schema template? If yes, how to validate the same?
You cannot reference instance data in your JSON Schema. This is considered business logic and is out side of the scope for JSON Schema.

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>

Json schema variable properties

Based some condition my IPV4 can have either 2 or 3 properties but those are required. How to define it. I tried below schema. I get error saying "JSON is valid against more than one schema from 'oneOf'. Valid schema indexes: 0, 1"
"IPv4Type": {
"type": "object",
"oneOf": [
{
"properties": {
"provider-address": {
"type": "string",
"format": "ipv4"
},
"customer-address": {
"type": "string",
"format": "ipv4"
},
"mask": {
"type": "number"
}
},
"required": [
"provider-address",
"customer-address",
"mask"
]
},
{
"properties": {
"provider-address": {
"type": "string",
"format": "ipv4"
},
"mask": {
"type": "number"
}
},
"required": [
"provider-address",
"mask"
]
}
]
}
A few ideas:
you can drop the oneOf , define your JSON in one object, with all 3 properties defined, but adding only "provider-address" and "mask" as required
defining "additionalProperties": false the the 2nd definition under oneOf
replacing oneOf with anyOf