jsonSchema attribute conditionally required

80,602

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.

Share:
80,602
tom redfern
Author by

tom redfern

I spend the time between weekends programming programmers. Want to get in touch? tom.redfern ((at)) Google's popular electronic mail service or https://www.linkedin.com/in/tomredfern/

Updated on June 17, 2021

Comments

  • tom redfern
    tom redfern almost 3 years

    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?

  • scubbo
    scubbo about 6 years
    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...
  • Jason Desrosiers
    Jason Desrosiers about 6 years
    @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
    Jason Desrosiers about 6 years
    @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... }
  • user4581301
    user4581301 almost 6 years
    @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?
  • Jason Desrosiers
    Jason Desrosiers almost 6 years
    @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.
  • Ron
    Ron almost 6 years
    @JasonDesrosiers wonderful answer, thank you. But how about if a conditional required property depend on a parent object property? I raised another question here, hope you can help to resolve it. Thanks.
  • Raptor
    Raptor over 5 years
    "allOf" helped me add multiple if conditons!
  • Scott B
    Scott B about 5 years
    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. stackoverflow.com/questions/54653168/…
  • Jason Desrosiers
    Jason Desrosiers about 5 years
    @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.
  • GGirard
    GGirard about 5 years
    @JasonDesrosiers do you know about any official documentation about Implications, especially their usage you demonstrated in the second case and the third, Enum, case?
  • Jason Desrosiers
    Jason Desrosiers about 5 years
    @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).
  • Alexey Shrub
    Alexey Shrub about 5 years
    @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.
  • Jason Desrosiers
    Jason Desrosiers about 5 years
    @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.
  • Flair
    Flair almost 5 years
    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?
  • Jason Desrosiers
    Jason Desrosiers almost 5 years
    @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. join.slack.com/t/json-schema/shared_invite/…
  • Flair
    Flair almost 5 years
  • JHH
    JHH over 4 years
    Googled for conditional json schema and was led here. Fantastic answer, covers all aspects with simple examples. Well done. 👍
  • S.A.
    S.A. over 3 years
    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.
  • Jaume Mussons Abad
    Jaume Mussons Abad over 3 years
    This is what i call a great and clear answer to the question, thanks