Search code examples
jsonjsonschemapython-jsonschema

Best way to pin point validation error cause with python jsonschema if/then/else statements


I am a big fan of python jsonshema. I recently found the if/then/else options which I really like.

However, I have a huge problem with it. Let's say I have:

{
    "required": ["PROP1"],
    "additionalProperties": True,
    "properties": { 
        "PROP1": {"enum": ["PROP1_CHOICE1", "PROP1_CHOICE2"]}
    },
    "allOf": [
        {
            "if": {
                "properties": {
                     "PROP1": {"enum": ["PROP1_CHOICE1"]}
                }
            },
            "then": {
                "required": ["PROP2"],
                "properties": {
                    "PROP2": {"type": "string"},
                }
            },
            "else": False
        },
        {
            "if": {
                "properties": {
                     "PROP1": {"enum": ["PROP1_CHOICE2"]}
                }
            },
            "then": {
                "required": ["PROP2"],
                "properties": {
                    "PROP2": {"type": "boolean"},
                }
            },
            "else": False
        },
    ]
}

Now assuming I test the following file:

{
    "PROP1": "PROP1_CHOICE1",
    "PROP2": true
}

I get an error as expected, which is:

jsonschema.exceptions.ValidationError: False schema does not allow {'PROP1': 'PROP1_CHOICE1', 'PROP2': True}

Failed validating None in schema['allOf'][1]:
    False

On instance:
    {'PROP1': 'PROP1_CHOICE1', 'PROP2': True}

However what I would really like is to obtain the error as if my "PROP_CHOICE2" would not be present in the if/then/else statement, which would be:

jsonschema.exceptions.ValidationError: True is not of type 'string'

Failed validating 'type' in schema['allOf'][0]['then']['properties']['PROP2']:
    {'type': 'string'}

On instance['PROP2']:
    True

I did find a work around for this which is to execute the if/then/else statements sequentially like this:

{
        "required": ["PROP1"],
        "additionalProperties": True,
        "properties": { 
            "PROP1": {"enum": ["PROP1_CHOICE1", "PROP1_CHOICE2"]}
        },
        "if": {
            "properties": {
                "PROP1": {"enum": ["PROP1_CHOICE1"]}
            }
        },
        "then": {
            "required": ["PROP2"],
            "properties": {
                "PROP2": {"type": "string"},
            }
        },
        "else": {
            "if": {
                "properties": {
                        "PROP1": {"enum": ["PROP1_CHOICE2"]}
            }
            },
            "then": {
                "required": ["PROP2"],
                "properties": {
                    "PROP2": {"type": "boolean"},
                }
            },
            "else": False
        }
}

This does resolve my issue but now the issue becomes the indentation level which can quickly increase if you have multiple if/then/else statements.

Is there a way to get a code similar to the first code sample of this question with the output of the second code sample (specific error message without increasing indentation level similar to a switch/case statement) ?

Please note that this issue is particularly problematic for big shema (if I keep first code sample than the error can't pinpoint the specific schema issue and if I put in place my solution, then my indentation level becomes crappy).


Solution

  • With the input from @Jason Desrosiers I now get the expected output as desired and found a way to keep the else statements (so I can deal with all forgotten enum options). Here is the new definition:

    {
        "required": ["PROP1"],
        "additionalProperties": True,
        "properties": { 
            "PROP1": {"enum": ["PROP1_CHOICE1", "PROP1_CHOICE2", "PROP_CHOICE_3"]}
        },
        "allOf": [
            {
                "if": {
                    "properties": {
                         "PROP1": {"const": "PROP1_CHOICE1"}
                    }
                },
                "then": {
                    "required": ["PROP2"],
                    "properties": {
                        "PROP2": {"type": "string"},
                    }
                }
            },
            {
                "if": {
                    "properties": {
                         "PROP1": {"const": "PROP1_CHOICE2"}
                    }
                },
                "then": {
                    "required": ["PROP2"],
                    "properties": {
                        "PROP2": {"type": "boolean"},
                    }
                },
                
            },
            {
                "else": False
            }
        ]
    }
    

    Thx @Jason Desrosiers

    With this I now get (as expected, Yeah!):

    jsonschema.exceptions.ValidationError: True is not of type 'string'
    
    Failed validating 'type' in schema['allOf'][0]['then']['properties']['PROP2']:
        {'type': 'string'}
    
    On instance['PROP2']:
        True
    

    And let's say I try forgotten "PROP_CHOICE_3" if statement I now get:

    jsonschema.exceptions.ValidationError: 'PROP1_CHOICE3' is not one of ['PROP1_CHOICE1', 'PROP1_CHOICE2', 'PROP_CHOICE_3']
    
    Failed validating 'enum' in schema['properties']['PROP1']:
        {'enum': ['PROP1_CHOICE1', 'PROP1_CHOICE2', 'PROP_CHOICE_3']}
    
    On instance['PROP1']:
        'PROP1_CHOICE3'