Json-schema required/non-required & allowed valeus for fields depend on the values of other fields - jsonschema

I have three fields, foo, bar, baz. bar depends on foo, baz depends on bar:
foo is bool
if foo is not provided bar & baz are forbidden
foo = true: bar is required enum with values bar1 & bar2
foo = false: bar & baz are forbidden
foo = true & bar = bar1: baz is a required object with required field baz1 and non-required field baz2 both string
foo = true & bar = bar2: baz is a required object with required field baz3 string
So I started building this iteratively. So far, I've got
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"foo": {
"type": "boolean"
}
},
"allOf": [
{
"if": {
"not": {
"required": ["foo"]
}
},
"then": {
"not": {
"required": ["bar", "baz"]
}
}
},
{
"if": {
"properties": {
"foo": {
"const": true
}
},
"required": ["foo"]
},
"then": {
"properties": {
"bar": {
"enum": ["bar1", "bar2"]
}
},
"required": ["bar"]
}
},
{
"if": {
"properties": {
"foo": {
"const": false
}
},
"required": ["foo"]
},
"then": {
"not": {
"required": ["bar", "baz"]
}
}
},
{
"if": {
"properties": {
"bar": {
"const": "bar1"
}
},
"required": ["bar"]
},
"then": {
"properties": {
"baz": {
"type": "object",
"properties": {
"baz1": { "type": "number" },
"baz2": { "type": "string" }
},
"required": ["baz1", "baz2"],
"additionalProperties": false
}
},
"required": ["baz"]
}
},
{
"if": {
"properties": {
"bar": {
"const": "bar2"
}
},
"required": ["bar"]
},
"then": {
"properties": {
"baz": {
"type": "object",
"properties": {
"baz3": { "type": "number" },
"baz4": { "type": "string" }
},
"required": ["baz3", "baz4"],
"additionalProperties": false
}
},
"required": ["baz"]
}
}
]
}
It correctly validates all combinations I've tried so far except when baz is present without foo & bar or with foo = false & no bar, which both validate to true even though I'd expect it to be false as both bar & baz are set as not require if foo is not required & if foo is false. What am I missing?

You should split up the required clauses so they check one keyword at a time.
"required": ["bar", "baz"] will be false if neither 'bar' nor 'baz' are present, which is what you want, but it will also be false if one property is present and the other is not, which is not (because you then wrap that check with a "not", making the "if" condition true).
Therefore, change that check to:
"allOf": [
{ "required": ["bar"] },
{ "required": ["baz"] }
]

Based on Ether's answer I've got this, it appears to be working as intended, so I'm providing it for ease of references for anybody who might need it in future:
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"foo": {
"type": "boolean"
}
},
"allOf": [
{
"if": {
"not": {
"required": ["foo"]
}
},
"then": {
"allOf": [
{
"not": {
"required": ["bar"]
}
},
{
"not": {
"required": ["baz"]
}
}
]
}
},
{
"if": {
"properties": {
"foo": {
"const": true
}
},
"required": ["foo"]
},
"then": {
"properties": {
"bar": {
"enum": ["bar1", "bar2"]
}
},
"required": ["bar"]
}
},
{
"if": {
"properties": {
"foo": {
"const": false
}
},
"required": ["foo"]
},
"then": {
"allOf": [
{
"not": {
"required": ["bar"]
}
},
{
"not": {
"required": ["baz"]
}
}
]
}
},
{
"if": {
"properties": {
"bar": {
"const": "bar1"
}
},
"required": ["bar"]
},
"then": {
"properties": {
"baz": {
"type": "object",
"properties": {
"baz1": { "type": "number" },
"baz2": { "type": "string" }
},
"required": ["baz1", "baz2"],
"additionalProperties": false
}
},
"required": ["baz"]
}
},
{
"if": {
"properties": {
"bar": {
"const": "bar2"
}
},
"required": ["bar"]
},
"then": {
"properties": {
"baz": {
"type": "object",
"properties": {
"baz3": { "type": "number" },
"baz4": { "type": "string" }
},
"required": ["baz3", "baz4"],
"additionalProperties": false
}
},
"required": ["baz"]
}
}
]
}

Related

Easy way to group custom defined types to use for reference?

How to reference types by group instead of doing it individually?
For example here, I want "sum" and "subtract" types to accept types that are "number"-related types only("number", "sum", "subtract"). While "concatenate" can accept both "number" and "string"-related types. Including their selves. This will be really helpful for me if in case I added more types and want them to be automatically included.
"sum": {
"title": "sum",
"type": "object",
"properties": {
"values": {
"type": "array",
"items": {
"anyOf": [
{
"type": "number"
},
{
"$ref": "#/definitions/sum"
},
{
"$ref": "#/definitions/subtract"
}
]
}
}
},
"required": [
"values"
]
},
"subtract": {
"title": "subtract",
"type": "object",
"properties": {
"minuend": {
"type": "number"
},
"subtrahend": {
"type": "number"
}
},
"required": [
"minuend",
"subtrahend"
]
},
"concatenate": {
"title": "concatenate",
"type": "object",
"properties": {
"strings": {
"type": "array",
"items": {
"anyOf": [
{
"type": "number"
},
{
"type": "string"
},
{
"$ref": "#/definitions/sum"
},
{
"$ref": "#/definitions/subtract"
}
]
}
}
},
"required": [
"strings"
]
}
}
Here's the full schema:
{
"definitions": {
"sum": {
"title": "sum",
"type": "object",
"properties": {
"values": {
"type": "array",
"items": {
"anyOf": [
{
"type": "number"
},
{
"$ref": "#/definitions/sum"
},
{
"$ref": "#/definitions/subtract"
}
]
}
}
},
"required": [
"values"
]
},
"subtract": {
"title": "subtract",
"type": "object",
"properties": {
"minuend": {
"type": "number"
},
"subtrahend": {
"type": "number"
}
},
"required": [
"minuend",
"subtrahend"
]
},
"concatenate": {
"title": "concatenate",
"type": "object",
"properties": {
"strings": {
"type": "array",
"items": {
"anyOf": [
{
"type": "number"
},
{
"type": "string"
},
{
"$ref": "#/definitions/sum"
},
{
"$ref": "#/definitions/subtract"
}
]
}
}
},
"required": [
"strings"
]
}
},
"title": "Step",
"type": "object",
"properties": {
"value": {
"oneOf": [
{
"type": "number",
"minimum": 0
},
{
"$ref": "#/definitions/sum"
},
{
"$ref": "#/definitions/subtract"
}
]
}
}
}

JSON Schema - How to narrow down possible array object values

How can the following requirements be configured in a JSON schema.
I have the following JSON
{
"orderMethods": [
{
"requestType": "some service",
"deployType": "MANUAL",
"deployLabel": "some label"
},
{
"requestType": "some service",
"deployType": "MANUAL",
"deployLabel": "some label"
},
{
"requestType": "some service",
"deployType": "AUTO",
"deploySubType": "REST",
"deployLabel": "some label",
"deployConfig": "some config"
},
{
"requestType": "some service",
"deployType": "AUTO",
"deploySubType": "DB",
"deployLabel": "some label",
"deployConfig": "some config"
}
]
}
Requirements:
only one deployType = "MANUAL" is allowed
only one deployType = "AUTO" is allowed
additionally, only one deploySubType = "REST or "DB" is allowed
Currently I have the following JSON schema. Unfortunately the above JSON is valid. How can the above requirements be addressed?
{
"$schema": "http://json-schema.org/draft-07/schema",
"type": "object",
"properties": {
"orderMethods": {
"$ref": "#/definitions/OrderMethods"
}
},
"additionalProperties": false,
"definitions": {
"OrderMethods": {
"type": "array",
"items": {
"$ref": "#/definitions/OrderMethod"
}
},
"OrderMethod": {
"oneOf": [
{
"$ref": "#/definitions/OrderMethodManualDeploy"
},
{
"$ref": "#/definitions/OrderMethodAutoDeploy"
}
],
"uniqueItems": true
},
"OrderMethodAutoDeploy": {
"oneOf": [
{
"$ref": "#/definitions/OrderMethodAutoRestDeploy"
},
{
"$ref": "#/definitions/OrderMethodAutoDBDeploy"
}
],
"uniqueItems": true
},
"OrderMethodManualDeploy": {
"type": "object",
"properties": {
"requestType": {
"type": "string"
},
"deployType": {
"const": "MANUAL"
},
"deployLabel": {
"type": "string"
}
},
"required": [
"requestType",
"deployType",
"deployLabel"
],
"additionalProperties": false
},
"OrderMethodAutoRestDeploy": {
"type": "object",
"properties": {
"requestType": {
"type": "string"
},
"deployType": {
"const": "AUTO"
},
"deploySubType": {
"const": "REST"
},
"deployLabel": {
"type": "string"
},
"deployConfig": {
"type": "string"
}
},
"additionalProperties": false,
"required": [
"requestType",
"deployType",
"deploySubType",
"deployLabel",
"deployConfig"
]
},
"OrderMethodAutoDBDeploy": {
"type": "object",
"properties": {
"requestType": {
"type": "string"
},
"deployType": {
"const": "AUTO"
},
"deploySubType": {
"const": "DB"
},
"deployConfig": {
"type": "string"
},
"deployLabel": {
"type": "string"
}
},
"additionalProperties": false,
"required": [
"requestType",
"deployType",
"deploySubType",
"deployLabel",
"deployConfig"
]
}
}
}
Thanks for your help/ideas.

Is there a way to have a rule in one object that references properties from a different object?

I could not find any examples of this so I'm assuming it's not possible, but want to confirm.
I have a main schema that references other schemas:
https://www.jsonschemavalidator.net/s/4aLvXa4I
{
"$defs": {
"mainSchema": {
"$id": "https://example.com/person.schema.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"type": "object",
"properties": {
"objectOne": {
"$ref": "#/$defs/objectOne"
},
"objectTwo": {
"$ref": "#/$defs/objectTwo"
}
}
},
"objectOne": {
"type": "object",
"properties": {
"checkThisValue": {
"type": "string",
"enum": [
"one",
"two",
"three"
]
}
}
},
"objectTwo": {
"type": "object",
"properties": {
"whenSettingThisValue": {
"type": "string",
"enum": [
"A",
"B",
"C"
]
}
}
}
},
"$ref": "#/$defs/mainSchema"
}
I want to define this rule:
IF objectOne.checkThisValue == one
THEN objectTwo.whenSettingThisValue MUST == A
Same for two=>B and three=>C
Is this possible somehow? How do I reference objectOne properties inside of objectTwo?
Edit
I tried to create an if rule for objectTwo that references objectOne here, but my syntax is wrong because it's not working. whenSettingThisValue is set to C and it's saying valid when it should be invalid: https://www.jsonschemavalidator.net/s/NNjEIhWW
"objectTwo": {
"type": "object",
"properties": {
"whenSettingThisValue": {
"type": "string",
"enum": [
"A",
"B",
"C"
]
}
},
"if": {
"properties": {
"objectOne": {
"checkThisValue": {
"const": "one"
}
}
}
},
"then": {
"properties": {
"objectTwo": {
"whenSettingThisValue": {
"const": "A"
}
}
}
}
}
},
I also tried using a oneOf rule in mainSchema where both the subschemas are used, but it is not working either (this example should be invalid): https://www.jsonschemavalidator.net/s/WkmMasDC
"mainSchema": {
"$id": "https://example.com/person.schema.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"type": "object",
"properties": {
"objectOne": {
"$ref": "#/$defs/objectOne"
},
"objectTwo": {
"$ref": "#/$defs/objectTwo"
}
},
"oneOf": [
{
"type": "object",
"properties": {
"objectOne": {
"checkThisValue": {
"const": "one"
}
},
"objectTwo": {
"whenSettingThisValue": {
"const": "A"
}
}
}
}
]
},
Solution
Per answer my OneOf rule was malformed. This correctly checks a value in one object is set in response to another. I thought because I was referencing them in another schema with $ref I would have to do something special, but I don't.
https://www.jsonschemavalidator.net/s/HcVhrShk
"oneOf": [
{
"type": "object",
"properties": {
"objectOne": {
"properties": {
"checkThisValue": {
"const": "one"
}
}
},
"objectTwo": {
"properties": {
"whenSettingThisValue": {
"const": "A"
}
}
}
}
},
{
"type": "object",
"properties": {
"objectOne": {
"properties": {
"checkThisValue": {
"const": "two"
}
}
},
"objectTwo": {
"properties": {
"whenSettingThisValue": {
"const": "B"
}
}
}
}
}
]
},
Yes you can. There is an if/then/else construct which takes schemas as its arguments, so you can define a rule "if property A exists with value X, then property B must exist with value Y" etc.
There are some examples here:
https://json-schema.org/understanding-json-schema/reference/conditionals.html#if-then-else
edit:
in your re-edit, change the oneOf clause to this:
"oneOf": [
{
"type": "object",
"properties": {
"objectOne": {
"properties": {
"checkThisValue": {
"const": "one"
}
}
}
}
},
{
"type": "object",
"properties": {
"objectTwo": {
"properties": {
"whenSettingThisValue": {
"const": "A"
}
}
}
}
}
]

Generic json schema for two condition based responses

I have an API returning response based on input type.
For a single filter I get
{
"intervalData": [
{
"timestamp": "2021-05-25T06:00:00.000Z",
"result": {
"Attempts": 309194
}
}
],
"intervalTotals": {
"Attempts": 4719471
}
}
For multiple filter I get
{
"multiFilterData": [
{
"filter": {
"id": 111111
},
"intervalData": {
"timestamp": "2021-05-25T20:41:39.000Z",
"result": {
"Attempts": 7902
}
}
},
{...}
]
}
That means the response will either have (intervalData,intervalTotals) OR multiFilterData.
Question:
Is it possible to have a generic schema to handle this or must I have two separate schemas?
How to make sure that if one combination is present other should not be there?
I tried with below schema on https://www.jsonschemavalidator.net/. The validation fails if there is some other keys listed under the required, unfortunately the validation succeeds if one of the entry from oneOf is missing. EDIT1 below
{
"$schema": "http://json-schema.org/draft-07/schema",
"type": "object",
"oneOf": [
{
"required": [
"intervalData",
"intervalTotals",
]
},
{
"required": [
"multiFilterData",
]
}
],
"properties": {
"intervalData": {
"type": "array",
"additionalItems": false,
"items": {
"type": "object",
"required": [
"timestamp",
"result"
],
"properties": {
...
},
"additionalProperties": false
}
},
"intervalTotals": {
"type": "object",
"required": [
"Attempts"
],
"properties": {
"Attempts": {
"type": "integer"
}
},
"additionalProperties": false
},
"multiFilterData": {
"type": "array",
"additionalItems": false,
"items": {
"type": "object",
"required": [
"filter",
"intervalData"
],
"properties": {
"filter": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "integer"
}
},
"additionalProperties": false
},
"intervalData": {
"type": "object",
"required": [
"timestamp",
"result"
],
"properties": {
"timestamp": {
"type": "string"
},
"result": {
"type": "object",
"required": [
"Attempts"
],
"properties": {
...
},
"additionalProperties": false
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
}
},
"additionalProperties": false
}
EDIT 1: This schema also passes even though the first required is missing an element on the list.
{
"$schema": "http://json-schema.org/draft-07/schema",
"type": "object",
"oneOf": [
{
"required": [
"intervalData"
]
},
{
"required": [
"multiFilterData",
]
}
],
"properties": {
"intervalData": {...},
"intervalTotals": {...},
"multiFilterData": {...}
},
"additionalProperties": false
}

SWITCH in json schema (v5 proposal)

I'm newbie about npm ajv
I have a question:
How many "switch" in a object json?
example:
var schema = {
"type": "object",
"switch": [
{
"if": {
"properties": {
"powerLevel": {"constant": false}
}
},
"then": {
"required": ["disbelief"]
}
},
{
"then": {
"required": ["confidence"]
}
}
],
"switch": [
{
"if": {
"properties": {
"power": {"constant": false}
}
},
"then": {
"required": ["disb"]
}
},
{
"then": {
"required": ["conf"]
}
}
]
};
I test with schema above in this link
it's just check end switch.
please help me! thanks!
You cannot have two keywords switch in the same object.
In this particular instance you can merge the "cases" in one switch:
{
"type": "object",
"switch": [
{
"if": { "properties": { "powerLevel": {"constant": false} } },
"then": { "required": ["disbelief"] }
},
{
"if": { "properties": { "power": {"constant": false} } },
"then": { "required": ["disb"] }
},
{
"then": {
"oneOf": [
{ "required": ["confidence"] },
{ "required": ["conf"] }
]
}
}
]
}
In general case you can use keywords allOf, anyOf, oneOf to merge two schemas containing duplicate keywords between them.