Json - 使用 Python 递归验证模式

Json - recursively validate schema with Python

我正在尝试使用 Python 中的 jsonschema 模块针对模板 JSON 模式递归验证自定义 JSON 模式 3.

自定义 JSON 看起来像这样:

{
  "endpoint": "rfc",
  "filter_by": ["change_ref", "change_i"],
  "expression": [
    {
      "field": "first_name",
      "operator": "EQ",
      "value": "O'Neil"
    },
    "AND",
    [
      {
        "field": "last_name",
        "operator": "NEQ",
        "value": "Smith"
      },
      "OR",
      {
        "field": "middle_name",
        "operator": "EQ",
        "value": "Sam"
      }
    ]
  ],
  "limit_results_to": "2"
} 

可以通过添加多个 ANDs 和 ORs 来进一步概括以上内容 => 我的问题与递归相关。

我试图验证此架构的模板在以下代码段中:

import json
import jsonschema


def get_data(file):
    with open(file) as data_file:
        return json.load(data_file)


def json_schema_is_valid():
    data = get_data("other.json")
    valid_schema = {
        "type": "object",
        "required": ["endpoint", "filter_by", "expression", "limit_results_to"],
        "properties": {
            "endpoint": {
                "type": "string",
                "additionalProperties": False
            },
            "filter_by": {
                "type": ["string", "array"],
                "additionalProperties": False
            },
            "limit_results_to": {
                "type": "string",
                "additionalProperties": False
            },
            "expression": {
                "type": "array",
                "properties": {
                    "field": {
                        "type": "string",
                        "additionalProperties": False
                    },
                    "operator": {
                        "type": "string",
                        "additionalProperties": False
                    },
                    "value": {
                        "type": "string",
                        "additionalProperties": False
                    }
                },
                "required": ["field", "operator", "value"]
            }
        }
    }
    return jsonschema.validate(data, valid_schema)


if __name__ == '__main__':
    print(json_schema_is_valid())

现在,似乎有些不对劲,因为当我 运行 上面的代码时,我得到 None 这可能(不是)没问题。当我试图在不允许的情况下修改 propertytype 时,我没有得到任何异常。我的模板有问题吗? Here, it looks like the expression properties are not parsed. More, I read here 我可以使我的模板使用 '$ref': '#' 递归验证我的自定义 JSON 模式,但我不太明白如何使用它。有人可以给我一些提示吗?

您的模式看起来有效,但不包括递归部分。查看 GitHub 上 jsonschema.validate 的源代码,我们可以看到该代码没有 return。所以我认为可以安全地假设您验证的方式将使用类似的东西:

try:
    jsonschema.validate(json_data, schema)
except ...:
    print('invalid json')
else:
    print('valid json')

要创建递归,您应该定义两个。我从 Recursive JSON Schema 中找到了如何做到这一点。你只需要做几个定义。首先是您的正常比较。这几乎只是将您当前的定义移到它自己的定义中。

{
    "definitions": {
        "comparison": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
                "field": {
                    "type": "string"
                },
                "operator": {
                    "type": "string"
                },
                "value": {
                    "type": "string"
                }
            },
            "required": ["field", "operator", "value"]
        }
    }
}

执行递归布尔值比较有点困难。我不知道如何将数组限制为三个项目,其中第二个是不同的类型,所以我选择使用一个对象,它具有三个明确定义的项目。此外,第一项和最后一项应具有相同的类型,以便您可以进行 (a and b) or (c and d) 之类的比较。所以我得到了以下架构:

{
    "definitions": {
        "comparison": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
                "field": {
                    "type": "string"
                },
                "operator": {
                    "type": "string"
                },
                "value": {
                    "type": "string"
                }
            },
            "required": ["field", "operator", "value"]
        },
        "booleanComparison": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
                "item1": {
                    "type": "object",
                    "oneOf": [
                        {"$ref": "#/definitions/comparison"},
                        {"$ref": "#/definitions/booleanComparison"}
                    ]
                },
                "operator": {
                    "type": "string"
                },
                "item2": {
                    "type": "object",
                    "oneOf": [
                        {"$ref": "#/definitions/comparison"},
                        {"$ref": "#/definitions/booleanComparison"}
                    ]
                }
            },
            "required": ["item1", "operator", "item2"]
        }
    },
    "type": "object",
    "properties": {
        "endpoint": {
            "type": "string"
        },
        "filter_by": {
            "type": ["string", "array"]
        },
        "limit_results_to": {
            "type": "string",
            "additionalProperties": false
        },
        "expression": {
            "type": "object",
            "oneOf": [
                {"$ref": "#/definitions/comparison"},
                {"$ref": "#/definitions/booleanComparison"}
            ]
        }
    },
    "required": ["endpoint", "filter_by", "expression", "limit_results_to"]
}

说明以下内容有效:

{
    "endpoint": "rfc",
    "filter_by": ["change_ref", "change_i"],
    "limit_results_to": "2",
    "expression": {
        "item1": {
            "field": "first_name",
            "operator": "EQ",
            "value": "O'Neil"
        },
        "operator": "AND",
        "item2": {
            "item1": {
                "field": "last_name",
                "operator": "NEQ",
                "value": "Smith"
            },
            "operator": "OR",
            "item2": {
                "field": "middle_name",
                "operator": "EQ",
                "value": "Sam"
            }
        }
    }
}

然而说如果你把"value": "Sam"改成"value": true是无效的,因为它是错误的类型。所以它似乎递归地按预期工作。