问题描述
我想利用我的架构中定义的默认值。我发现python-jsonschema常见问题解答中已经有一个示例: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,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","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
}
},"additionalProperties": False
},{
"type": "object","properties": {
"class_name": {
"const": "another_class"
},"another_property": {
"type": "number","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','another_property': 1}
因此,当anyOf
到达通常适合该对象的第二个子模式时,另一个密钥已添加到实例,导致验证失败,并且不允许additionalProperties
。结果,在anyOf
中找不到有效的架构,并且出现了上述错误。
那么如何解决此问题?我的方法是让anyOf
在迭代子方案列表时存储实例的值。如果子模式不匹配,则应还原该子模式所做的所有更改。不幸的是,直到现在,我还无法实现这种行为。
作为参考,这是我最近的尝试:
def extend_with_default(validator_class):
validate_properties = validator_class.VALIDATORS["properties"]
def set_defaults(validator,):
yield error
def any_of(validator,subschemas,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}]}
我不明白为什么。我猜字典在通过验证器时会发生变化,因为它们是可变的。因此,尝试重置实例在全局上下文中可能无法达到预期的效果。但是,我不知道如何解决此问题。有人可以协助吗?
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)