Asked  7 Months ago    Answers:  2   Viewed   32 times

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?

 Answers

21

Depending on your situation, there are a few different approaches. I can think of four different ways to conditionally require a field.

Dependencies

The dependencies keyword is a conditional variation of the required keyword. Foreach property in dependencies, 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" }
  },
  "dependencies": {
    "foo": { "required": ["bar"] }
  }
}

There is also a short form if the schema only contains the required keyword.

{
  "type": "object",
  "properties": {
    "foo": { "type": "string" },
    "bar": { "type": "string" }
  },
  "dependencies": {
    "foo": ["bar"]
  }
}

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.

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

If-Then-Else

A relatively new addition to JSON Schema (draft-07) adds the if, then and else keywords. 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.

Tuesday, June 1, 2021
 
talkhabi
answered 7 Months ago
46

Yes, the same approach applies. You just need to nest schemas a little deeper.

{
  "type": "object",
  "properties": {
    "os": { "enum": ["macOs", "windows"] },
    "specs": {
      "type": "object",
      "properties": {
        "macModel": { "enum": ["macbook air", "macbook pro", "macbook"] },
        "memory": { "type": "number" }
      }
    }
  },
  "allOf": [{ "$ref": "#/definitions/os-macOs-requires-macModel" }],
  "definitions": {
    "os-macOs-requires-macModel": {
      "anyOf": [
        { "not": { "$ref": "#/definitions/os-macOs" } },
        { "$ref": "#/definitions/requires-macModel" }
      ]
    },
    "os-macOs": {
      "properties": {
        "os": { "const": "macOs" }
      },
      "required": ["os"]
    },
    "requires-macModel": {
      "properties": {
        "specs": {
          "required": ["macModel"]
        }
      }
    }
  }
}

Notice that in the /definitions/requires-macModel schema it has to dig into the "specs" property and place the required there instead of at the top level as it is in the flat case.

I've used the implication pattern for this example, but the same approach can be taken with if-then if you prefer that approach and have access to a draft-07 validator.

Thursday, August 19, 2021
 
Sugrue
answered 4 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share