我尝试创建一个验证器,在验证期间从 JSON 模式设置默认值。
我发现了这个问题:尝试在Python中制作JSON Schema验证器来设置默认值并对其进行了一些调整。 由于我使用“jsonschema==3.2.0”,所以我想出了这样的代码:
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}
)
除了一个对我来说很重要的案例外,它运行良好。我写了这样一个测试来证明它不适用于我的情况:
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},
}
测试失败并出现此断言错误:
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}}
?
因此我们可以看到,尽管第一个子模式无效,但其“params”中的默认值(“q”)已设置。 通过一些调试,我发现当您仅覆盖“属性”验证器时,它缺少上下文。因此,当第一个子模式“params”得到验证时,我没有上下文告诉我“p”参数验证失败,并且我们仍然处于相同的子模式中。
请让我了解我可以尝试什么。
可以在原来的jsonschema中找到文字Q&A doc:
在此代码中,我们在每个对象之前添加默认属性 属性经过验证,因此默认值本身需要 在该模式下有效。
您正在使用扩展验证器运行验证和 set_default。这就是为什么字典仍然更新的原因。您可能希望首先使用单独的验证器来运行它们以进行验证。
您是否考虑过先填充实例中的所有默认值,然后再验证它,而不是在验证期间设置默认值?
您可以使用
fill_default
中的
jsonschema-fill-default
用其架构填充现有实例中所有缺失的默认值:
pip install jsonschema-fill-default
from jsonschema_fill_default import fill_default
schema = {
"type": "object",
"properties": {
"key_1": {},
"key_2": {
"type": "string",
"default": "do_not_overwrite_if_key_exists",
},
"key_3": {
"type": "string",
"default": "use_it_if_key_does_not_exist",
},
},
"required": ["key_1"],
}
json_dict = {"key_1": "key_1_value", "key_2": "key_2_value"}
fill_default(json_dict, schema) # Mutates instance!
>>> json_dict
{"key_1": "key_1_value", "key_2": "key_2_value", "key_3": "use_it_if_key_does_not_exist"}
您显示的函数仅填充“属性”中的默认值,而
fill_default
适用于"properties"
、"allOf"
、"anyOf"
、"oneOf"
、"dependentSchemas"
、"if-then(-else)"
、"prefixItems"
的所有嵌套组合, 和"items"
2020-12 草案 JSON Schema 的关键字。