在验证期间设置JSON模式的默认值
我尝试制作一个验证器,该验证器将在验证期间从JSON模式设置默认值。
我发现了一个问题: python中的JSON模式验证器设置默认值并将其调整了一点。 由于我使用“ 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}}
?
因此,我们可以看到,尽管第一个亚chema是无效的,但从其“ params”中的默认值(“ q”)是放。 通过一些调试,我发现当您仅覆盖“属性”验证器时,它就会缺乏上下文。因此,当第一个子场“参数”得到验证时,我没有上下文告诉我“ p”参数验证失败,我们仍处于同一子策略中。
请给我任何有关我可以尝试的东西的见解。
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
You can find the words in the original jsonschema
您正在使用扩展验证器运行验证和set_default。这就是为什么DICT仍会更新的原因。您可能需要使用分离的验证器来运行它们,仅首先进行验证。
You can find the words in the original jsonschema Q&A doc:
And you are running the validation and set_default with the extended validator. That is why the dict is updated still. You might want to run them with a separated validator for validation only first.
您是否考虑过在实例中首先填写所有默认值然后验证它,而不是在验证期间设置默认值?
您可以使用
fill_default
来自/code>
(我创建的一个软件包)以填充具有其架构的现有实例中的所有丢失默认值:
您显示的函数仅填充“属性”中的默认值
“ properties”的嵌套组合
,“ allof”
,“ anyof”
,“ Oneof” ,“ DepententsChemas”
,“ if-then(-else)”
,“ prefixItems”
和“ tocke>'items'
<< a href =“ https://json-schema.org/draft/2020-12” rel =“ nofollow noreferrer”>草稿2020-12 json schema 。Instead of setting defaults during validation, have you considered first filling all defaults in an instance and then validating it?
You can use
fill_default
fromjsonschema-fill-default
(a package I created) to fill all missing defaults in an existing instance with its schema:The function you show only fills defaults in "properties", whereas
fill_default
works with all nested combinations of"properties"
,"allOf"
,"anyOf"
,"oneOf"
,"dependentSchemas"
,"if-then(-else)"
,"prefixItems"
, and"items"
keywords of Draft 2020-12 JSON Schema.