我使用JSON Schema FAQ中稍微修改过的代码版本来创建一个设置默认值的验证器:
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},
)
DefaultValidatingDraft4Validator = extend_with_default(Draft4Validator)
我有一个像这样的JSON模式:
{'definitions': {
'obj': {'additionalProperties': False,
'properties': {
'foo': {'default': None, 'oneOf': [{'type': 'null'}, {'type': 'string'}]},
'bar': {'default': None, 'oneOf': [{'type': 'null'}, {'type': 'string'}]},
'baz': {'default': None, 'oneOf': [{'type': 'null'}, {'type': 'string'}]},
'children': {'default': None, 'oneOf': [
{'type': 'null'},
{
'items': {'$ref': '#/definitions/obj'},
'minItems': 1,
'type': 'array'
}
]}},
'required': ['foo', 'bar', 'baz'],
'type': 'object'}},
'oneOf': [
{'$ref': '#/definitions/obj'},
{
'items': {'$ref': '#/definitions/obj'},
'minItems': 1,
'type': 'array'
}
]
}
基本上,有一个对象可以有foo/bar/baz
个字段,整个实例可以是其中一个对象,也可以是它们的列表。此外,每个对象都可以在children
字段中包含子对象列表。
当我尝试针对单个对象运行此代码时,它可以正常工作,但是当我有一个对象列表时它会失败:
In [22]: DefaultValidatingDraft4Validator(schema).validate({'foo': 'hi'})
In [23]: DefaultValidatingDraft4Validator(schema).validate([{'foo': 'hi'}, {'baz': 'bye'}])
...
AttributeError: 'list' object has no attribute 'setdefault'
"孩子"字段,我需要一种方法来处理模式验证的每个级别的列表。有没有办法正确地做到这一点?
答案 0 :(得分:0)
在验证程序中,导致异常的list
是有效元素。
因此,您需要通过更改以下内容排除list
:
if "default" in subschema:
instance.setdefault(property_, subschema["default"])
为:
if "default" in subschema and not isinstance(instance, list):
instance.setdefault(property_, subschema["default"])
这就是让两个测试用例通过所需的全部内容。
from jsonschema import Draft4Validator, 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 and not isinstance(instance, list):
instance.setdefault(property_, subschema["default"])
for error in validate_properties(
validator, properties, instance, schema,
):
yield error
return validators.extend(
validator_class, {"properties": set_defaults},
)
FillDefaultValidatingDraft4Validator = extend_with_default(Draft4Validator)
test_schema = {
'definitions': {
'obj': {'additionalProperties': False,
'properties': {
'foo': {'default': None, 'oneOf': [{'type': 'null'}, {'type': 'string'}]},
'bar': {'default': None, 'oneOf': [{'type': 'null'}, {'type': 'string'}]},
'baz': {'default': None, 'oneOf': [{'type': 'null'}, {'type': 'string'}]},
'children': {'default': None, 'oneOf': [
{'type': 'null'},
{
'items': {'$ref': '#/definitions/obj'},
'minItems': 1,
'type': 'array'
}
]}
},
'required': ['foo', 'bar', 'baz'],
'type': 'object'}
},
'oneOf': [
{'$ref': '#/definitions/obj'},
{
'items': {'$ref': '#/definitions/obj'},
'minItems': 1,
'type': 'array'
}
]
}
for test_data in ({'foo': 'hi'}, [{'foo': 'hi'}, {'baz': 'bye'}],
[{'children': [{'foo': 'hi'}, {'baz': 'bye'}]}]):
FillDefaultValidatingDraft4Validator(test_schema).validate(test_data)
print(test_data)
{'foo': 'hi', 'bar': None, 'baz': None, 'children': None}
[
{'foo': 'hi', 'bar': None, 'baz': None, 'children': None},
{'baz': 'bye', 'foo': None, 'bar': None, 'children': None}
]
[
{'children': [
{'foo': 'hi', 'bar': None, 'baz': None, 'children': None},
{'baz': 'bye', 'foo': None, 'bar': None, 'children': None}
], 'foo': None, 'bar': None, 'baz': None}
]