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

时间:2016-12-22 19:57:49

标签: 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)

我有一个像这样的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'

"孩子"字段,我需要一种方法来处理模式验证的每个级别的列表。有没有办法正确地做到这一点?

1 个答案:

答案 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}
]