Jsonschema RefResolver用于在Python中解析多个ref。

5

如何使用jsonschema.RefResolver验证模式中的多个引用?

如果文件中只有一个引用,则我的验证脚本可以正常工作。现在我有两个或三个引用在不同的目录中,这些引用在模式中。

base_dir = '/schema/models/'
with open (os.path.join(base_dir, 'Defined.json')) as file_object:
    schema = json.load(file_object)
    resolver = jsonschema.RefResolver('file://' + base_dir + '/' + 'Fields/Ranges.json', schema)
    jsonschema.Draft4Validator(schema, resolver=resolver).validate(data)

我的JSON模式:

{
  "properties": {
    "description": {
        "type": "object",
        "after": {"type": ["string", "null"]},
        "before": {"type": "string"}
      },
      "width": {"type": "number"} ,
      "range_specifier": {"type": "string"},
      "start": {"type": "number", "enum" : [0, 1] } ,
      "ranges": {
        "$ref": "Fields/Ranges.json"
      },
      "values": {
        "$ref": "Fields/Values.json"
      }
  }
}

我的问题是,我是否应该有两个解析器,一个用于范围,另一个用于值,并在Draft4Validator中分别调用这些解析器?或者有更好的方法来处理这个问题吗?

4个回答

9

我自己也花了几个小时来解决同样的问题,所以我希望这个解决方法对其他人有用。

def validate(schema_search_path, json_data, schema_id):
    """
    load the json file and validate against loaded schema
    """
    try:
        schemastore = {}
        schema = None
        fnames = os.listdir(schema_search_path)
        for fname in fnames:
            fpath = os.path.join(schema_search_path, fname)
            if fpath[-5:] == ".json":
                with open(fpath, "r") as schema_fd:
                    schema = json.load(schema_fd)
                    if "id" in schema:
                        schemastore[schema["id"]] = schema

        schema = schemastore.get("http://mydomain/json-schema/%s" % schema_id)
        Draft4Validator.check_schema()
        resolver = RefResolver("file://%s.json" % os.path.join(schema_search_path, schema_id), schema, schemastore)
        Draft4Validator(schema, resolver=resolver).validate(json_data)
        return True
    except ValidationError as error:
        # handle validation error 
        pass
    except SchemaError as error:
        # handle schema error
        pass
    return False

每个应用于路径解析的JSON模式都有一个ID元素,必须将其作为schema_id参数传递以进行验证。

  "id": "http://mydomain/json-schema/myid"

所有模式都被加载到一个字典中,然后作为存储库传递给解析器。在您的示例中,您还应该从其他目录加载模式。


到目前为止,这是我看过的最好的答案。我更进一步,避免了解析器的前两个参数的初始化,这需要id是一个url:resolver = jsonschema.RefResolver("", "", schemastore) 运行良好。它可以防止代码尝试取消引用schemastore中的其他模式时出现KeyError异常。 - Laurent Le Meur

2
我把整个模式都存储在名为api-spec.yaml的YAML文件中,该文件位于Python包的根目录下。YAML文件符合Swagger 3.0规范。将此示例重新配置以验证根模式中描述的任何对象应该是微不足道的。
注意:此示例要求您从命令行执行以下操作:
pip install pyyaml, jsonschema

在名为cn的包中,YAML文件在__init__.py中加载:
import yaml, os
__all__ = ['api_swagger']
with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'api-spec.yaml')) as file:
    api_swagger = yaml.load(file, Loader=yaml.SafeLoader)

以下是Python单元测试中的示例代码:

import unittest
from cn import api_swagger


class ValidateSwaggerSchema_Tests(unittest.TestCase):

        def test_validation_using_jsonschema_RefResolver___should_pass(self):
            # setup
            from cn import api_swagger
            schemastore = {
                '': api_swagger,
            }
            resolver = jsonschema.RefResolver(
                base_uri='',
                referrer=api_swagger,
                store=schemastore)

            # setup: the schemas used when validating the data
            ParsedMessageSchema = api_swagger['components']['schemas']['ParsedMessage']
            FillSchema = api_swagger['components']['schemas']['Fill']

            # validate: ParsedMessageSchema
            validator = jsonschema.Draft7Validator(ParsedMessageSchema, resolver=resolver)
            validator.validate({
                'type': 'fill',
                'data': [{
                    'order_id': 'a_unique_order_id',
                    'exchange_id': 'bittrex',
                    'market_id': 'USD-BTC',
                    'side': 'sell',
                    'price': 11167.01199693,
                    'amount': 0.00089773,
                    'type': 'limit',
                    'status': 'filled',
                    'leaves_amount': 0.0,
                    'cumulative_amount': 0.00089773,
                    'timestamp': '2019-07-13T20:17:01.480000',
                }]
            })

            # validate: FillSchema
            validator = jsonschema.Draft7Validator(FillSchema, resolver=resolver)
            validator.validate(
                {
                    'order_id': 'a_unique_order_id',
                    'exchange_id': 'bittrex',
                    'market_id': 'USD-BTC',
                    'side': 'sell',
                    'price': 11167.01199693,
                    'amount': 0.00089773,
                    'type': 'limit',
                    'status': 'filled',
                    'leaves_amount': 0.0,
                    'cumulative_amount': 0.00089773,
                    'timestamp': '2019-07-13T20:17:01.480000',
                }
            )

0
你可以使用 Python 库 "pyjn" (pip install pyjn) 并在三行内完成此操作。
from pyjn import pyjn
pyjn=pyjn()
json_pathtest='C:/Users/Entity.json'
print(pyjn.refsolver(json_pathtest))


0
我在每个模式的`"$id"`和每个`"$ref"`中使用了完整的URI,并且在使用`RefResolver`时不需要担心相对路径。
模式A,位于`/my-schemas/A.schema.json`中找到:
{
    "$id": "https://mycompany.com/my-schemas/A.schema.json",
    ...
}

位于<repo-root>/my-schemas/one-more-level/B.schema.json的Schema B:
{
    "$id": "https://mycompany.com/my-schemas/one-more-level/B.schema.json",
    ...
    {
        "$ref": "https://mycompany.com/my-schemas/A.schema.json"
    }
    ...
}

然后,我将所有的模式加载到一个存储中,以它们的完整ID作为键值。
schema_store: Dict[str, dict] = {}
for path_, _, files in walk("<repo-root>/my-schemas/"):
    for file in files:
        if file.endswith(".schema.json"):
            absfile = path.join(path_, file)
            with open(absfile) as f:
                schema = load(f)
                schema_store[schema["$id"]] = schema

然后,使用RefResolver(文档稀缺)对实例进行验证,例如与B进行验证。
schema = schema_store["https://mycompany.com/my-schemas/one-more-level/B.schema.json"]
validator = Draft202012Validator(
    schema=schema,
    resolver=RefResolver(base_uri="", referrer=schema, store=schema_store),  # type: ignore
)
validator.validate(instance={ ... })

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接