在验证期间从 JSON 模式设置默认值

问题描述 投票:0回答:2

我尝试创建一个验证器,在验证期间从 JSON 模式设置默认值。

我发现了这个问题:尝试在Python中制作JSON Schema验证器来设置默认值并对其进行了一些调整。 由于我使用“jsonschema==3.2.0”,所以我想出了这样的代码:

def _with_default_setter_extension(validator_class):
    """Extend validator class with defaults setter.

    With this extension, the validator class will set all defaults from a
    schema being validated to a validated instance.
    """

    def _set_defaults(validator, properties, instance, schema):
        if not validator.is_type(instance, "object"):
            return

        valid = True
        for prop, subschema in properties.items():
            if prop in instance:
                for error in validator.descend(
                    instance[prop],
                    subschema,
                    path=prop,
                    schema_path=prop,
                ):
                    valid = False
                    yield error

        # set defaults only when validation is successful
        if valid:
            # set root default when instance is empty
            if not instance and "default" in schema:
                instance.update(schema["default"])
                return

            for prop, subschema in properties.items():
                if "default" in subschema and not isinstance(instance, list):
                    instance.setdefault(prop, subschema["default"])

    return jsonschema.validators.extend(
        validator_class, {"properties": _set_defaults}
    )

除了一个对我来说很重要的案例外,它运行良好。我写了这样一个测试来证明它不适用于我的情况:

def test_defaults_from_oneOf_only_defaults_from_valid_schema_are_set():
    """When oneOf is used, I expect only defaults from the valid subschema to be set."""
    schema = {
        "oneOf": [
            {
                "properties": {
                    "p": {"enum": ["one"]},
                    "params": {"properties": {"q": {"default": 1}}},
                }
            },
            {
                "properties": {
                    "p": {"enum": ["two"]},
                    "params": {"properties": {"w": {"default": 2}}},
                }
            },
        ],
    }
    assert _VALIDATOR.validate({"p": "two", "params": {}}, schema) == {
        "p": "two",
        "params": {"w": 2},
    }

测试失败并出现此断言错误:

AssertionError: assert {'p': 'two', 'params': {'q': 1, 'w': 2}} == {'p': 'two', 'params': {'w': 2}}
  +{'p': 'two', 'params': {'q': 1, 'w': 2}}
  -{'p': 'two', 'params': {'w': 2}}
  Full diff:
  - {'p': 'two', 'params': {'w': 2}}
  + {'p': 'two', 'params': {'q': 1, 'w': 2}}
  ?

因此我们可以看到,尽管第一个子模式无效,但其“params”中的默认值(“q”)已设置。 通过一些调试,我发现当您仅覆盖“属性”验证器时,它缺少上下文。因此,当第一个子模式“params”得到验证时,我没有上下文告诉我“p”参数验证失败,并且我们仍然处于相同的子模式中。

请让我了解我可以尝试什么。

python jsonschema python-jsonschema
2个回答
0
投票

可以在原来的jsonschema中找到文字Q&A doc:

在此代码中,我们在每个对象之前添加默认属性 属性经过验证,因此默认值本身需要 在该模式下有效。

您正在使用扩展验证器运行验证和 set_default。这就是为什么字典仍然更新的原因。您可能希望首先使用单独的验证器来运行它们以进行验证。


0
投票

您是否考虑过先填充实例中的所有默认值,然后再验证它,而不是在验证期间设置默认值?

您可以使用

fill_default
 中的 
jsonschema-fill-default
用其架构填充现有实例中所有缺失的默认值:

pip install jsonschema-fill-default
from jsonschema_fill_default import fill_default

schema = {
    "type": "object",
    "properties": {
        "key_1": {},
        "key_2": {
            "type": "string",
            "default": "do_not_overwrite_if_key_exists",
        },
        "key_3": {
            "type": "string",
            "default": "use_it_if_key_does_not_exist",
        },
    },
    "required": ["key_1"],
}

json_dict = {"key_1": "key_1_value", "key_2": "key_2_value"}

fill_default(json_dict, schema)  # Mutates instance!
>>> json_dict

{"key_1": "key_1_value", "key_2": "key_2_value", "key_3": "use_it_if_key_does_not_exist"}

您显示的函数仅填充“属性”中的默认值,而

fill_default
适用于
"properties"
"allOf"
"anyOf"
"oneOf"
"dependentSchemas"
"if-then(-else)"
"prefixItems"
的所有嵌套组合, 和
"items"
2020-12 草案 JSON Schema 的关键字。

© www.soinside.com 2019 - 2024. All rights reserved.