尝试在 Python 中制作 JSON Schema 验证器以设置默认值

shr*_*oud 5 python jsonschema

我正在使用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)
Run Code Online (Sandbox Code Playgroud)

我有一个像这样的 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'
    }
  ]
}
Run Code Online (Sandbox Code Playgroud)

所以基本上,有一个对象可以有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'
Run Code Online (Sandbox Code Playgroud)

对于“children”字段,我需要一种方法来处理架构验证的每个级别的列表。有没有办法正确地做到这一点?

Ste*_*uch 3

在验证器中,list导致异常的元素是有效元素。

需要改变:

因此,您需要list通过更改来排除:

if "default" in subschema:
    instance.setdefault(property_, subschema["default"])
Run Code Online (Sandbox Code Playgroud)

到:

if "default" in subschema and not isinstance(instance, list):
    instance.setdefault(property_, subschema["default"])
Run Code Online (Sandbox Code Playgroud)

这就是让两个测试用例通过所需的全部内容。

代码:

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)
Run Code Online (Sandbox Code Playgroud)

测试代码:

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)
Run Code Online (Sandbox Code Playgroud)

结果:

{'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}
]
Run Code Online (Sandbox Code Playgroud)