TheChetan
TheChetan

Reputation: 4596

Json Schema if/then not working as expected

I'm trying to validate a simple json schema and I simply can't seem to get this right. I'm trying to implement this piece of code into JsonSchema, but it doesn't validate correctly.

Code:

if (field1 is "REAL") {
    then both attributes.A and attributes.B are present
    if (attributes.field2 is "true") {
        then attributes.C is also present
    }
}

I've converted this to a JsonSchema as follows:

{
  "$schema": "https://json-schema.org/draft/2019-09/schema#",
  "properties": {
    "field1": {},
    "attributes": {
      "type": "object",
      "properties": {
        "field2": {
          "type": "string",
          "default": "false"
        }
      }
    }
  },
  "allOf": [
    {
      "if": {
        "properties": {
          "field1": {
            "enum": [
              "REAL"
            ]
          }
        }
      },
      "then": {
        "properties": {
          "attributes": {
            "$id": "#/properties/realAttributes",
            "type": "object",
            "title": "The Attributes Schema",
            "required": [
              "A",
              "B"
            ],
            "properties": {
              "A": {},
              "B": {},
              "C": {},
              "D": {},
              "field2": {}
            }
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "field1": {
            "enum": ["REAL"]
          },
          "attributes": {
            "properties": {
              "field2": {
                "enum": ["true"]
              }
            }
          }
        }
      },
      "then": {
        "properties": {
          "attributes": {
            "required": [
              "C"
            ]
          }
        }
      }
    }
  ]
}

The second if validation in allOf is not working correctly and I can't figure out why. It's failing when I pass field1, but not field2. Play with the failing implementation here.

I would expect this to pass, but it reports an error saying that C is missing. The failing case:

{
  "field1": "REAL",
    "attributes": {
      "A": 4,
        "B": 5
    }
}

Upvotes: 0

Views: 3309

Answers (1)

Relequestual
Relequestual

Reputation: 12295

JSON Schema works by applying the schema (and subschemas) to instance locations.

The conditional keywords if/then/else work by applying the schema value of if to the instance location, and depending on the result, applying then or else if provided.

The key here is JSON Schema is a constraints based language, and anything you don't specify / constrain is allowable.

Let's take your if subschema which isn't working as you expect...

{
  "properties": {
    "field1": {
      "enum": ["REAL"]
    },
    "attributes": {
      "properties": {
        "field2": {
          "enum": ["true"]
        }
      }
    }
  }
}

Let's take a look at what you've specified here. But first, let's check what properties does...

Validation succeeds if, for each name that appears in both the
instance and as a name within this keyword's value, the child
instance for that name successfully validates against the
corresponding schema.

https://datatracker.ietf.org/doc/html/draft-handrews-json-schema-02#section-9.3.2.1

In your if subschema, you've said, "if field1 exists, it should be "REAL" " AND "if the object has a property attributes, and if that object value has a property field2, then it should be "true" ".

The problem is, if field1 doesn't exist, and if attributes doesn't exist, then the subschema will still pass validation.

If you require the property to be present, you must require it.

Let's take a look at your schema having added the required keyword...

{
  "$schema": "https://json-schema.org/draft/2019-09/schema#",
  "properties": {
    "field1": {},
    "attributes": {
      "type": "object",
      "properties": {
        "field2": {
          "type": "string",
          "default": "false"
        }
      }
    }
  },
  "allOf": [
    {
      "if": {
        "required": [
          "field1"
        ],
        "properties": {
          "field1": {
            "enum": [
              "REAL"
            ]
          }
        }
      },
      "then": {
        "properties": {
          "attributes": {
            "$id": "#/properties/realAttributes",
            "type": "object",
            "title": "The Attributes Schema",
            "required": [
              "A",
              "B"
            ],
            "properties": {
              "A": {},
              "B": {},
              "C": {},
              "D": {},
              "field2": {}
            }
          }
        }
      }
    },
    {
      "if": {
        "required": [
          "field1"
        ],
        "properties": {
          "field1": {
            "enum": [
              "REAL"
            ]
          },
          "attributes": {
            "required": [
              "field2"
            ],
            "properties": {
              "field2": {
                "enum": [
                  "true"
                ]
              }
            }
          }
        }
      },
      "then": {
        "properties": {
          "attributes": {
            "required": [
              "C"
            ]
          }
        }
      }
    }
  ]
}

Playground: https://www.jsonschemavalidator.net/s/mAPFXugk

Now the subschemas have required the keywords too, the validation works as you expect.

If you run into problems using conditionals in JSON Schema, you can test your assumptions by changing a subschema to false, such as the value of then. You can use this technique to check which conditions are met or not.

Upvotes: 1

Related Questions