Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JsonSchema: Validate type based on value of another property

I am using the following schema to validate my json:

{
    "$schema": "http://json-schema.org/schema#",
    "title": " Rules",
    "description": "Describes a set of rules",
    "type": "object",
    "properties": {
        "rules": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "precedence": {
                        "type": "number",
                        "minimum": 0
                    },
                    "conditions": {
                        "type": "array",
                        "items": {
                            "type": "object",
                            "properties": {
                                "field": {
                                    "type": "string",
                                    "enum": [ "Name", "Size" ]
                                },
                                "relation": {
                                    "type": "string",
                                    "enum": [ "is", "is not", "is not one of", "is one of" ]
                                },
                                "value": {
                                    "type": ["array", "string", "number"]
                                }
                            },
                            "required": ["field", "relation", "value"],
                            "additionalProperties": false
                        }
                    }                       
                },
                "required": ["precedence", "conditions"],
                "additionalProperties": false
            }
        }
    },
    "required": ["rules"],
    "additionalProperties": false
}

I want to set up a dependency to validate that when the value of the relation property has the value is one of or the value is not one of, then the type of the value property can only be array

For example, the following json should not validate because it uses the relation value is not one of and the value property is not an array:

{
    "rules": [{
            "precedence": 0,
            "conditions": [{
                    "field": "Name",
                    "relation": "is not one of",
                    "value": "Mary"
                }
            ]
        }
    ]
}

Is it possible to set up dependencies to validate this way?

like image 510
Joel Harris Avatar asked Apr 11 '18 21:04

Joel Harris


2 Answers

The best way to solve these kinds of problems is to separate the complex validation from the rest of the schema using definitions and include it with an allOf. In this solution, I use implication to enforce the validation.

{
  "type": "object",
  "properties": {
    "rules": {
      "type": "array",
      "items": { "$ref": "#/definitions/rule" }
    }
  },
  "required": ["rules"],
  "definitions": {
    "rule": {
      "type": "object",
      "properties": {
        "precedence": { "type": "number", "minimum": 0 },
        "conditions": {
          "type": "array",
          "items": { "$ref": "#/definitions/condition" }
        }
      },
      "required": ["precedence", "conditions"]
    },
    "condition": {
      "type": "object",
      "properties": {
        "field": { "enum": ["Name", "Size"] },
        "relation": { "enum": ["is", "is not", "is not one of", "is one of"] },
        "value": { "type": ["array", "string", "number"] }
      },
      "required": ["field", "relation", "value"],
      "allOf": [{ "$ref": "#/definitions/array-condition-implies-value-is-array" }]
    },
    "array-condition-implies-value-is-array": {
      "anyOf": [
        { "not": { "$ref": "#/definitions/is-array-condition" } },
        { "$ref": "#/definitions/value-is-array" }
      ]
    }
    "is-array-condition": {
      "properties": {
        "relation": { "enum": ["is not one of", "is one of"] }
      },
      "required": ["relation"]
    },
    "value-is-array": {
      "properties": {
        "value": { "type": "array" }
      }
    }
  }
}
like image 189
Jason Desrosiers Avatar answered Sep 27 '22 23:09

Jason Desrosiers


If you are able to use the latest draft-7 version of JSON Schema, you can use if then else, as per https://datatracker.ietf.org/doc/html/draft-handrews-json-schema-validation-00#section-6.6

Although, using oneOf is also a valid approach, it might not be as clear to someone else inspecting your schema at a later date.

I've copied an example from an answer to another question:

If the "foo" property equals "bar", Then the "bar" property is required

{
  "type": "object",
  "properties": {
    "foo": { "type": "string" },
    "bar": { "type": "string" }
  },
  "if": {
    "properties": {
      "foo": { "enum": ["bar"] }
    }
  },
  "then": { "required": ["bar"] }
}

(You may want to check the draft support of the library you are using.)

like image 38
Relequestual Avatar answered Sep 27 '22 21:09

Relequestual