169

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?

tom redfern
  • 30,562
  • 14
  • 91
  • 126

3 Answers3

432

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.

Jason Desrosiers
  • 22,479
  • 5
  • 47
  • 53
  • This is a wonderful reference, thank you! Out of interest, why did you use a single-value enum rather than a const in the third example? Also, do you have any stylistic advice for having multiple `if-then-else` in the same schema? At the moment I have an `allOf` at the top-level, which contains one `allOf` per conditional, but it feels like I'm missing something... – scubbo Apr 04 '18 at 20:38
  • @scubbo I wrote this before the `const` keyword existed (it was added draft-06). Maybe next time I feel like this question needs an update, I'll change those to `const`. – Jason Desrosiers Apr 05 '18 at 00:16
  • 11
    @scubbo I'm not a fan of the `if-then-else` keywords and I refuse to use them. But, if you choose to use it, I suggest always wrapping them in an `allOf` that contains only those three keywords. `{ ...other_keywords..., "allOf": [{ "if": ..., "then": ..., "else": ... }], ...more_keywords... }` – Jason Desrosiers Apr 05 '18 at 00:20
  • 3
    @Jason Why not a fan of `if...`? I think a brief opinion on this in your answer would be entirely justified. Or is it a long story? – Clay Bridges May 14 '18 at 13:46
  • 11
    @ClayBridges The comment section isn't the right place for that discussion, but here's the short version. As a general rule, JSON Schema keywords are stateless. No Information other than the keyword value can be used to validate the instance. `if`, `then`, and `else` violate this rule because they depend on each other. – Jason Desrosiers May 15 '18 at 03:55
  • @JasonDesrosiers wonderful answer, thank you. But how about if a conditional required property depend on a parent object property? I raised another question [here](https://stackoverflow.com/questions/51203606/jsonschema-attribute-conditionally-required-depends-on-parent-object), hope you can help to resolve it. Thanks. – Ron Jul 06 '18 at 05:45
  • "allOf" helped me add multiple if conditons! – Ahsan Aug 14 '18 at 09:39
  • Would any of this work if you want to show/hide a form element based on value of a selected enum? I have a related unanswered question but this looks close. https://stackoverflow.com/questions/54653168/json-schema-form-condition-show-hide-based-on-enum-selected-item – Scott B Feb 13 '19 at 21:44
  • @ScottB I don't know. Form building is not part of the JSON Schema specification. You'll have to inquire about how to do it for the specific tool you are trying to use. There is no standard for form building. – Jason Desrosiers Feb 14 '19 at 00:42
  • @JasonDesrosiers do you know about any official documentation about `Implications`, especially their usage you demonstrated in the second case and the third, `Enum`, case? – GGirard Feb 27 '19 at 14:55
  • 6
    @GGirard, this is the best treatment of the use of these patterns in JSON Schema that I'm aware of. The boolean operations are officially documented but the rest is just math. `allOf` == AND, `anyOf` == OR, `oneOf` == XOR, and `not` == NOT. You can google "boolean algebra" for more resources on the math stuff (such as implication). – Jason Desrosiers Feb 27 '19 at 17:26
  • @jason-desrosiers do you post somewhere more detailed description about why you don't like if-then-else? As for me it's looks most readable. – Alexey Shrub Mar 26 '19 at 07:35
  • 2
    @AlexeyShrub I've been wanting to write about this for a while, but have been distracted by other things. I am a fan of the _idea_ of a conditional. It does make it easier for people to understand. My objection is to the way it was defined as three separate _stateful_ keywords (see previous comment). Having keywords that violate the architectural properties that other keywords follow makes JSON Schema validators harder to implement and less efficient. If conditionals were defined in a different way that was stateless, then I would have no objection. – Jason Desrosiers Mar 26 '19 at 15:59
  • I tried implementing that above in my own example where if a required key on the base level has a certain value, then another key on the base level is required to exist, but the four methods do not work. I am not sure why. Does it not work if the certain value is of type object? – Flair May 24 '19 at 18:25
  • @Flair, it sounds like you need some help applying the patterns. I'm happy to help, but the comments aren't a good place for that. I suggest you either create a new SO question (@ me and I'll check it out personally), or head over the the JSON Schema slack and we can discuss it there. https://join.slack.com/t/json-schema/shared_invite/enQtMjk1NDcyNDI2NTAwLTcyYmYwMjdmMmUxNzZjYzIxNGU2YjdkNzdlOGZiNjIwNDI2M2Y3NmRkYjA4YmMwODMwYjgyOTFlNWZjZjAyNjg – Jason Desrosiers May 24 '19 at 20:45
  • @JasonDesrosiers https://stackoverflow.com/questions/56299562/json-schema-required-if-key-exists-in-nested-object – Flair May 24 '19 at 21:10
  • 4
    Googled for conditional json schema and was led here. Fantastic answer, covers all aspects with simple examples. Well done. – JHH Sep 18 '19 at 05:05
  • How could I extend this to a case where several properties conditionally influence the "required" rule? For example assuming that initially nothing is required, how to implement the following: Having keys A, B, C, D ... if "A" is "foo", C is required, if "B" is "foo", D is required, and if both "A" and "B" are "foo", both C and D are required. else, nothing is required. – S.A. Oct 08 '20 at 11:52
  • This is what i call a great and clear answer to the question, thanks – Jaume Mussons Abad Dec 17 '20 at 09:25
  • As of 2022, the `dependencies` keyword is deprecated (and actually removed from the programs I'm using), now we need to use dependentRequired and dependentSchemas https://stackoverflow.com/questions/72889096/json-schema-validation-does-not-work-for-dependencies/72889164#72889164 – tobiasBora Jul 06 '22 at 19:55
5

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"]
  }
}
tobiasBora
  • 1,542
  • 14
  • 23
-2

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>