jsonvalidationjsonschemajson-schema-validator

How to set custom error message in jsonschema if/else


I have a JSON schema like this:

{
  "authentication": {
    // provider is an enum. Let's say it has A and B as values.
    // if A is chosen, jwt must have a value.
    // if B is chosen, jwt must NOT have a value
    // Let's say JWT has 3 requirede properties. audience, issuer, and baz
    "provider": "B",
    "jwt": { "audience": "foo", "issuer": "bar" }
  }
}

This is my current validation:

"allOf": [
  {
    "$comment": "We want the user to provide the JWT property when the provider requires it",
    "if": {
      "properties": {
        "provider": {
          "anyOf": [
            { "const": "A" },
          ]
        }
      },
      "required": ["provider"]
    },
    "then": {
      "required": ["jwt"]
    }
  },
  {
    "$comment": "We want JWT property to be null when a provider does not require it. This reduces bugs and confusion.",
    "if": {
      "properties": {
        "provider": {
          "not": {
            "anyOf": [
              { "const": "A" }
            ]
          }
        }
      },
      "required": ["provider"]
    },
    "then": {
      "properties": {
        "jwt": {
          "type": "null"
        }
      }
    }
  }
]

This works, but it isn't a great experience:

The only error that would make sense would be that jwt should not have a value because of the chosen provider. This is represented by Incorrect type. Expected "null"., but it isn't obvious enough.

So, my questions:


Solution

  • This doesn't provide custom error output, but the output is much cleaner

    Basically, you only check for the fail condition, if B then disallow jwt. Otherwise, require jwt and the root schema provides the constraints for jwt.

    {
      "$schema": "https://json-schema.org/draft/2020-12/schema",
      "required": ["authorization"],
      "properties": {
        "authorization": {
          "required": ["provider"],
          "properties": {
            "provider": {"type": "string"},
            "jwt": {
              "required": ["audience", "issuer", "baz"],
              "properties": {
                "audience": {},
                "issuer": {},
                "baz": {}
              }
            }
          },
          "allOf": [
            {
              "if": {
                "properties": {
                  "provider": {"const": "B"}
                } 
              },
              "then": {
                "properties": {"jwt": false}
              },
              "else": {
                "required": ["jwt"]
              }
            }
          ]
        }
      }
    }
    

    the only difference between null and disallowed would be changing the then statement

          "then": {
            "properties": {
              "jwt": {
                "const": null
              }
            }
          }
    
    authorization:
      provider: A
      jwt:
        audience: true
        issuer: true
        baz: true
    
    authorization:
      provider: B
    
    authorization:
      provider: B
      jwt: null