Json - 使用Python递归验证模式

时间:2017-08-28 12:09:07

标签: python json python-3.x jsonschema

我尝试使用Python 3中的jsonschema模块递归验证模板JSON模式的自定义JSON模式。

自定义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"
} 

通过添加多个ANDOR s =>可以进一步推广上述内容。我的问题与递归有关。

我尝试验证此架构的模板位于以下代码段中:

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可能(不)没问题。当我尝试修改typeproperty的某些不允许的内容时,我不会得到任何感知。我的模板中有什么问题吗? Here,看起来表达式属性未被解析。更多,我阅读here我可以使用'$ref': '#'递归验证我的自定义JSON架构,但我还不太了解如何使用它。有人可以给我一些提示吗?

1 个答案:

答案 0 :(得分:0)

您的架构看起来有效,不包括递归部分。查看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,则说它无效,因为它的类型错误。所以它似乎递归地工作。