6

I would like to apply an additional "required" property in an array sub schema based on the presence of a property in the root schema. I have my schema set like this:

{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",
    "required":[
       "isParentDependency",
       "subArray"
    ],
    "properties": {
        "isParentDependency": {
            "$id": "#/properties/isParentDependency",
            "type": "boolean"
        },
        "subArray": {
            "$id": "#/properties/subArray",
            "type": "array",
            "items": {
                "$id": "#/properties/subArrayItem",
                "required": ["alwaysRequiredProp"],
                "dependencies": {
                    "isParentDependency":{
                        "required":["requiredPropIfIsParentDependency"]
                    }
                },
                "properties": {
                    "alwaysRequiredProp": {
                        "$id": "#/properties/subArray/items/properties/alwaysRequiredProp",
                        "type": "boolean"
                    },
                    "requiredPropIfIsParentDependency": {
                        "$id": "#/properties/subArray/items/properties/requiredPropIfIsParentDependency",
                        "type": "boolean"
                    }
                }
            }
        }
    }
}

Passing Cases

{
    "isParentDependency": false,
    "subArray": [{
        "alwaysRequiredProp": true
    }]
}
    "isParentDependency": true,
    "subArray": [{
        "alwaysRequiredProp": true,
        "requiredPropIfIsParentDependency":true
    }]
}

Failing Cases

{
    "isParentDependency": true,
    "subArray": [{
        "alwaysRequiredProp": true
    }]
}

Clearly this is not going to work but I have been unable to figure out how to make a pointer to the root schema (or apply an if/else type solution with a $ref)

Any guidance greatly appreciated!

Mike Miller
  • 3,071
  • 3
  • 25
  • 32
  • You can't "see up" the tree, so you have to define what you need at the root level and use subschemas. I'm happy to help, but could you clean up your example first please? Not complete JSON, and does not identify which draft it is using (I'm going to assume draft-7). Additionally, please provide a JSON instance for your pass and fail conditions (this will allow me to give you a quicker answer). – Relequestual Jul 22 '19 at 12:11
  • Thanks very much @Relequestual - I have (hopefully) fleshed out the Q with what you need – Mike Miller Jul 23 '19 at 08:25
  • Super. Working... – Relequestual Jul 23 '19 at 08:26

1 Answers1

9

With JSON Schema, each level of nested properties down the tree cannot see up the tree. Therefore you have to define your conditions at the top most level to which it needs to see. In this case, that's the root level.

Take a look at this schema.

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "definitions": {
    "subArray": {
      "type": "array",
      "items": {
        "required": [
          "alwaysRequiredProp"
        ],
        "properties": {
          "alwaysRequiredProp": {
            "type": "boolean"
          },
          "requiredPropIfIsParentDependency": {
            "type": "boolean"
          }
        }
      }
    }
  },
  "required": [
    "isParentDependency",
    "subArray"
  ],
  "properties": {
    "isParentDependency": {
      "type": "boolean"
    },
    "subArray": {
      "$ref": "#/definitions/subArray"
    }
  },
  "if": {
    "properties": {
      "isParentDependency": {
        "const": true
      }
    }
  },
  "then": {
    "properties": {
      "subArray": {
        "items": {
          "required": [
            "requiredPropIfIsParentDependency"
          ]
        }
      }
    }
  }
}

subArray has been moved to a definition and then referenced, so the conditional application logic can be seen easier.

The definition of subArray only determines the properties of the items structure if they are provided, and requires alwaysRequiredProp. It does not require requiredPropIfIsParentDependency.

The conditional application comes in using the if and then keywords.

If the if schema is true, then the else schema is applied.

The value of if is a schema. It is applicable to the root level. It requires isParentDependency be true (it does not require the property itself, because that is ALWAYS required. Requiring the property inside the if statement would NOT mean the property is always required, unless else was false, but we're not even using else here).

When isParentDependency is true, the then schema is applied, which looks down the tree to subArray's items, requiring requiredPropIfIsParentDependency.

You can test this working in browser using jsonschema.dev (preloaded with this schema and your fail instance).

Relequestual
  • 11,631
  • 6
  • 47
  • 83
  • You EAT JSONSchema!! Thats a great explanation - adding as a definition is the bit I had failed to grasp. Thanks very much! – Mike Miller Jul 23 '19 at 08:49
  • You're welcome Mike. I am one of the spec writers and admins so I know it pretty well. Feel free to join our slack via the website discussion link. – Relequestual Jul 23 '19 at 09:05
  • Well then thanks for all your hard work! I am using it for the validation of state in React across many interrelated components and is proving to be a very tidy solution – Mike Miller Jul 23 '19 at 09:09
  • Welcome! We are unfunded, so if you want to support us, see the link in my profile. We hope to have an OpenCollective soon too. – Relequestual Jul 23 '19 at 09:10
  • Done and cheap at twice the price! – Mike Miller Jul 23 '19 at 09:18
  • Very kind! Thanks. The door is always open in the Slack. We pick up all SO questions tagged with `jsonschema`! – Relequestual Jul 23 '19 at 09:22