I try to make a validator that would set defaults from a JSON schema during validation.
I found this question: Trying to make JSON Schema validator in Python to set default values and adjusted it a bit. Since I use "jsonschema==3.2.0", I came up with such a code:
def _with_default_setter_extension(validator_class):
"""Extend validator class with defaults setter.
With this extension, the validator class will set all defaults from a
schema being validated to a validated instance.
"""
def _set_defaults(validator, properties, instance, schema):
if not validator.is_type(instance, "object"):
return
valid = True
for prop, subschema in properties.items():
if prop in instance:
for error in validator.descend(
instance[prop],
subschema,
path=prop,
schema_path=prop,
):
valid = False
yield error
# set defaults only when validation is successful
if valid:
# set root default when instance is empty
if not instance and "default" in schema:
instance.update(schema["default"])
return
for prop, subschema in properties.items():
if "default" in subschema and not isinstance(instance, list):
instance.setdefault(prop, subschema["default"])
return jsonschema.validators.extend(
validator_class, {"properties": _set_defaults}
)
It works good except one case which is important for me. I wrote such a test to prove it does not work for my case:
def test_defaults_from_oneOf_only_defaults_from_valid_schema_are_set():
"""When oneOf is used, I expect only defaults from the valid subschema to be set."""
schema = {
"oneOf": [
{
"properties": {
"p": {"enum": ["one"]},
"params": {"properties": {"q": {"default": 1}}},
}
},
{
"properties": {
"p": {"enum": ["two"]},
"params": {"properties": {"w": {"default": 2}}},
}
},
],
}
assert _VALIDATOR.validate({"p": "two", "params": {}}, schema) == {
"p": "two",
"params": {"w": 2},
}
The test fails with this assertion error:
AssertionError: assert {'p': 'two', 'params': {'q': 1, 'w': 2}} == {'p': 'two', 'params': {'w': 2}}
+{'p': 'two', 'params': {'q': 1, 'w': 2}}
-{'p': 'two', 'params': {'w': 2}}
Full diff:
- {'p': 'two', 'params': {'w': 2}}
+ {'p': 'two', 'params': {'q': 1, 'w': 2}}
?
So we can see, that despite the first subschema is invalid, the default value ("q") from its "params" is set. With some debugging, I discovered that when you override only the "properties" validator, it lacks context. So when the first subschema "params" gets validated, I have no context telling me that "p" param validation failed and we are still in the same subschema.
Please, give me any insight into what I could try.