Search code examples
jsonschemajson-schema-validatorpython-jsonschema

Python jsonschema unable to validate JSON schema conditional nested properties


Reposting the question How to implement conditional nested properties with JSON Schema (marked as duplicate though its a completely different problem) My JSON schema reads below tried based on : JSON Schema if/then require nested object

{
  "$id": "myschema.json",
  "if":{
    "type":"object",
    "properties": {
      "common_data": {
        "type":"object",
        "properties":{
          "remote_os": {
              "type":"object",
              "required":["common_data"],
              "const": "Linux"
            }
          },
          "required":["remote_os"]
      }
    }
  },
  "then": {
    "type":"object",
    "properties": {
      "file": {
        "type": "string",
        "pattern": "^(.*.)(bin)$"
        }
      }
  },
  "else": {
    "type":"object",
    "properties": {
      "file": {
        "type": "string",
        "pattern": "^(.*.)(exe)$"
        }
      }
  }
}

Basically adding the if-else logic to make sure for remote_os=Linux file should end up with .bin and remote_os=Windows file should end up with .exe Now I am trying to validate against below data

{
  "common_data": {
    "remote_os": "Linux"
  },
  "file": "abc.bin"
}

Getting error : [<ValidationError: "'abc.bin' does not match '^(.*.)(exe)$'">]

When tried to debug what properties python jsonschema is trying build on top of this schema to validate my data. Got this

properties {'common_data': {'type': 'object', 'properties': {'remote_os': {'type': 'object', 'required': ['common_data'], 'const': 'Linux'}}, 'required': ['remote_os']}}
properties {'remote_os': {'type': 'object', 'required': ['common_data'], 'const': 'Linux'}}
properties {'file': {'type': 'string', 'pattern': '^(.*.)(exe)$'}}

So its always matching against the 'pattern': '^(.*.)(exe)$' irrespective of remote_os. Looking for some guidance, how to address this issue.


Solution

  • You're close, but there are a few problems.

    First, let's assess if our assumption is right... It looks like if is failing, and triggering the else schema value to be applied rather than the then schema value. We can check this by changing if to simply true.

    Yup, the then subschema works OK when applied.

    OK, so let's remove parts of the nested schema till it works as we expect...

    Ah... Look. remote_os in our data is a string, but it has been defined in the schema as being an object. Remove type: object.

    Those required keywords don't match the data location... they just need to be moved UP to the correct level...

    We end up with this schema: (live demo: https://jsonschema.dev/s/CEwMw)

    {
      "$schema": "http://json-schema.org/draft-07/schema",
      "$id": "myschema.json",
      "if": {
        "type": "object",
        "required": [
          "common_data"
        ],
        "properties": {
          "common_data": {
            "type": "object",
            "required": [
              "remote_os"
            ],
            "properties": {
              "remote_os": {
                "const": "Linux"
              }
            }
          }
        }
      },
      "then": {
        "type": "object",
        "properties": {
          "file": {
            "type": "string",
            "pattern": "^(.*.)(bin)$"
          }
        }
      },
      "else": {
        "type": "object",
        "properties": {
          "file": {
            "type": "string",
            "pattern": "^(.*.)(exe)$"
          }
        }
      }
    }
    

    Further, another schema debugging tip, is take the if schema value out, and use that as your whole schema. That way you can find out why THAT is failing validation, rather than just guessing.

    These are easy mistakes to make! I made a few while trying to fix it. The important thing is to know how to debug your schema. Test assumptions, and recognise subschemas.