我想使用模式中定义的默认值。我发现python jsonschema faq中已经有一个示例:https://python-jsonschema.readthedocs.io/en/stable/faq/
该示例扩展了property
关键字的默认验证器,并根据需要设置默认值。但是,在同一模式中使用anyOf
关键字时,我遇到了一个问题
让我举个例子:
from jsonschema import Draft7Validator, validators
def extend_with_default(validator_class):
validate_properties = validator_class.VALIDATORS["properties"]
def set_defaults(validator, properties, instance, schema):
for property, subschema in properties.items():
if "default" in subschema:
instance.setdefault(property, subschema["default"])
for error in validate_properties(
validator, properties, instance, schema,
):
yield error
return validators.extend(
validator_class, {"properties": set_defaults},
)
DefaultValidatingDraft7Validator = extend_with_default(Draft7Validator)
obj = {
"my_list": [{"class_name": "some_class"}]
}
schema = {
"properties": {
"my_list": {
"type": "array",
"items": {
"anyOf": [
{
"type": "object",
"properties": {
"class_name": {
"const": "some_class"
},
"some_property": {
"type": "number",
"default": 1
}
},
"required": ["class_name", "some_property"],
"additionalProperties": False
},
{
"type": "object",
"properties": {
"class_name": {
"const": "another_class"
},
"another_property": {
"type": "number",
"default": 1
}
},
"required": ["class_name", "another_property"],
"additionalProperties": False
}
]
}
}
}
}
DefaultValidatingDraft7Validator(schema).validate(obj)
print(obj)
这个示例实际上是按照预期的方式工作的。运行它将提供以下输出:
{'my_list': [{'class_name': 'some_class', 'some_property': 1}]}
因此属性some_property
被正确设置为默认值1。但是,如果我们现在将对象中的class_name
更改为another_class
,这与anyOf
列表中的第二个条目相匹配,则会出现以下问题:
obj = {
"my_list": [{"class_name": "another_class"}]
}
=>
jsonschema.exceptions.ValidationError: {'class_name': 'another_class', 'some_property': 1, 'another_property': 1} is not valid under any of the given schemas
Failed validating 'anyOf' in schema['properties']['my_list']['items']:
{
"anyOf": [
{
"type": "object",
"properties": {
"class_name": {
"const": "some_class"
},
"some_property": {
"type": "number",
"default": 1
}
},
"required": ["class_name", "some_property"],
"additionalProperties": False
},
{
"type": "object",
"properties": {
"class_name": {
"const": "another_class"
},
"another_property": {
"type": "number",
"default": 1
}
},
"required": ["class_name", "another_property"],
"additionalProperties": False
}
]
}
On instance['my_list'][0]:
{'another_property': 1,
'class_name': 'another_class',
'some_property': 1}
在anyOf
列表上迭代时,第一个子模式已经更改了给定给anyOf
的实例。anyOf
验证器调用每个子模式的所有相关验证器,因此第一个子模式的properties
验证器将第一个子模式的默认值插入当前实例。当子模式的验证在anyOf
内未成功时,也会发生这种情况。因此,本例中不适用的第一个子模式插入了属性'some_property': 1
:
obj = {'class_name': 'another_class', 'some_property': 1, 'another_property': 1}
因此,现在当anyOf
到达通常适合对象的第二个子模式时,另一个键被添加到实例中,导致验证失败,并且不允许additionalProperties
。因此,在anyOf
中找不到有效的模式,我们得到了上述错误
那么如何解决这个问题呢?我的方法是让anyOf
在迭代子模式列表时存储实例的值。如果子模式不匹配,则应还原此子模式所做的所有更改。不幸的是,直到现在,我还不能实现这个行为
作为参考,以下是我最近的尝试:
def extend_with_default(validator_class):
validate_properties = validator_class.VALIDATORS["properties"]
def set_defaults(validator, properties, instance, schema):
for property, subschema in properties.items():
if "default" in subschema:
instance.setdefault(property, subschema["default"])
for error in validate_properties(
validator, properties, instance, schema,
):
yield error
def any_of(validator, subschemas, instance, schema):
instance_copy = instance.copy()
all_errors = []
for index, subschema in enumerate(subschemas):
errs = list(validator.descend(instance, subschema, schema_path=index))
if not errs:
break
instance = instance_copy # Make sure an instance that did not fit is not modified
all_errors.extend(errs)
else:
yield ValidationError(
"%r is not valid under any of the given schemas" % (instance,),
context=all_errors,
)
return validators.extend(
validator_class, {"properties": set_defaults, "anyOf": any_of},
)
从内部看,这似乎是可行的,验证也是可行的。但是由于某种原因obj
的内容现在是{"my_list": [{"class_name": "another_class"}]}
:
{'my_list': [{'class_name': 'another_class', 'some_property': 1}]}
我不明白为什么。我猜字典是在通过验证器时更改的,因为它们是可变的。因此,在全局上下文中,尝试重置实例可能不会产生预期效果。然而,我不知道如何解决这个问题。有人能帮忙吗
目前没有回答
相关问题 更多 >
编程相关推荐