如何在python-jsonschema文档中设置本地文件引用?

时间:2018-12-29 10:39:06

标签: python json jsonschema python-jsonschema

我有一组jsonschema兼容的文档。某些文档包含对其他文档的引用(通过$ref属性)。我不希望托管这些文档,以便可以通过HTTP URI访问它们。因此,所有参考文献都是相对的。所有文档都位于本地文件夹结构中。

如何使python-jsonschema了解如何正确使用本地文件系统来加载引用的文档?


例如,如果我有一个文件名为defs.json的文档,其中包含一些定义。然后,我尝试加载引用它的其他文档,例如:

{
  "allOf": [
    {"$ref":"defs.json#/definitions/basic_event"},
    {
      "type": "object",
      "properties": {
        "action": {
          "type": "string",
          "enum": ["page_load"]
        }
      },
      "required": ["action"]
    }
  ]
}

我收到一个错误RefResolutionError: <urlopen error [Errno 2] No such file or directory: '/defs.json'>

我在Linux机器上可能很重要。


(我将其写为问答,因为我很难弄清这个问题,observed other folks having trouble too。)

5 个答案:

答案 0 :(得分:7)

我很难确定如何针对一组$ref的架构进行解析(我是JSON架构的新手)。事实证明,关键是要创建一个RefResolver的{​​{1}},该store是从URL映射到架构的dict。 以@ devin-p的答案为基础:

import json

from jsonschema import RefResolver, Draft7Validator

base = """
{
  "$id": "base.schema.json",
  "type": "object",
  "properties": {
    "prop": {
      "type": "string"
    }
  },
  "required": ["prop"]
}
"""

extend = """
{  
  "$id": "extend.schema.json",
  "allOf": [
    {"$ref": "base.schema.json#"},
    {
      "properties": {
        "extra": {
          "type": "boolean"
        }
      },
    "required": ["extra"]
    }
  ]
}
"""

extend_extend = """
{
  "$id": "extend_extend.schema.json",
  "allOf": [
    {"$ref": "extend.schema.json#"},
    {
      "properties": {
        "extra2": {
          "type": "boolean"
        }
      },
    "required": ["extra2"]
    }
  ]
}
"""

data = """
{
"prop": "This is the property string",
"extra": true,
"extra2": false
}
"""

schema = json.loads(base)
extendedSchema = json.loads(extend)
extendedExtendSchema = json.loads(extend_extend)
schema_store = {
    schema['$id'] : schema,
    extendedSchema['$id'] : extendedSchema,
    extendedExtendSchema['$id'] : extendedExtendSchema,
}


resolver = RefResolver.from_schema(schema, store=schema_store)
validator = Draft7Validator(extendedExtendSchema, resolver=resolver)

jsonData = json.loads(data)
validator.validate(jsonData)

以上内容是用jsonschema==3.2.0构建的。

答案 1 :(得分:1)

您必须为每个使用相对引用的模式构建自定义jsonschema.RefResolver,并确保您的解析器知道给定模式在文件系统上的位置。

例如...

import os
import json
from jsonschema import Draft4Validator, RefResolver # We prefer Draft7, but jsonschema 3.0 is still in alpha as of this writing 


abs_path_to_schema = '/path/to/schema-doc-foobar.json'
with open(abs_path_to_schema, 'r') as fp:
  schema = json.load(fp)

resolver = RefResolver(
  # The key part is here where we build a custom RefResolver 
  # and tell it where *this* schema lives in the filesystem
  # Note that `file:` is for unix systems
  schema_path='file:{}'.format(abs_path_to_schema),
  schema=schema
)
Draft4Validator.check_schema(schema) # Unnecessary but a good idea
validator = Draft4Validator(schema, resolver=resolver, format_checker=None)

# Then you can...
data_to_validate = `{...}`
validator.validate(data_to_validate)

答案 2 :(得分:0)

紧跟@ chris-w提供的答案,我想对jsonschema 3.2.0做同样的事情,但他的答案并没有完全涵盖我,我希望这个答案可以帮助那些仍然对这个问题感兴趣的人帮助,但正在使用该软件包的最新版本。

要使用该库扩展JSON模式,请执行以下操作:

  1. 创建基本架构:
base.schema.json
{
  "$id": "base.schema.json",
  "type": "object",
  "properties": {
    "prop": {
    "type": "string"
    }
  },
  "required": ["prop"]
}
  1. 创建扩展架构
extend.schema.json
{
  "allOf": [
    {"$ref": "base.schema.json#"},
    {
      "properties": {
        "extra": {
          "type": "boolean"
        }
      },
    "required": ["extra"]
    }
  ]
}
  1. 创建要针对架构进行测试的JSON文件
data.json
{
  "prop": "This is the property",
  "extra": true
}
  1. 为基本架构创建RefResolver和Validator并使用它来检查数据
#Set up schema, resolver, and validator on the base schema
baseSchema = json.loads(baseSchemaJSON) # Create a schema dictionary from the base JSON file
relativeSchema = json.loads(relativeJSON) # Create a schema dictionary from the relative JSON file
resolver = RefResolver.from_schema(baseSchema) # Creates your resolver, uses the "$id" element
validator = Draft7Validator(relativeSchema, resolver=resolver) # Create a validator against the extended schema (but resolving to the base schema!)

# Check validation!
data = json.loads(dataJSON) # Create a dictionary from the data JSON file
validator.validate(data)

您可能需要对以上条目进行一些调整,例如不使用Draft7Validator。这应该适用于单级引用(扩展基础的子级),您将需要谨慎使用架构以及如何设置RefResolverValidator对象。

P.S。这是一个练习上面的片段。尝试修改data字符串以删除必需的属性之一:

import json

from jsonschema import RefResolver, Draft7Validator

base = """
{
  "$id": "base.schema.json",
  "type": "object",
  "properties": {
    "prop": {
      "type": "string"
    }
  },
  "required": ["prop"]
}
"""

extend = """
{
  "allOf": [
    {"$ref": "base.schema.json#"},
    {
      "properties": {
        "extra": {
          "type": "boolean"
        }
      },
    "required": ["extra"]
    }
  ]
}
"""

data = """
{
"prop": "This is the property string",
"extra": true
}
"""

schema = json.loads(base)
extendedSchema = json.loads(extend)
resolver = RefResolver.from_schema(schema)
validator = Draft7Validator(extendedSchema, resolver=resolver)

jsonData = json.loads(data)
validator.validate(jsonData)

答案 3 :(得分:0)

我的方法是将所有架构片段预加载到RefResolver缓存中。我创建了一个说明要点的要点:https://gist.github.com/mrtj/d59812a981da17fbaa67b7de98ac3d4b

答案 4 :(得分:0)

编辑

修复了对$ref模式的错误引用(base)。 更新了示例,以使用 the 文档中的示例:https://json-schema.org/understanding-json-schema/structuring.html

这只是@Daniel回答的另一个版本-对我来说是正确的。基本上,我决定在基本架构中定义$schema。然后释放其他模式,并在实例化 resolver 时进行清晰的调用。

  • RefResolver.from_schema()得到(1) some 模式以及(2)一个模式存储的事实对我来说还不是很清楚。 / em>“模式与此处相关。因此,您在下面看到的结构。

我有以下内容:

base.schema.json

{
  "$schema": "http://json-schema.org/draft-07/schema#"
}

definitions.schema.json

{
  "type": "object",
  "properties": {
    "street_address": { "type": "string" },
    "city":           { "type": "string" },
    "state":          { "type": "string" }
  },
  "required": ["street_address", "city", "state"]
}

address.schema.json

{
  "type": "object",

  "properties": {
    "billing_address": { "$ref": "definitions.schema.json#" },
    "shipping_address": { "$ref": "definitions.schema.json#" }
  }
}

我喜欢此设置有两个原因:

    RefResolver.from_schema()
  1. 然后我会从库提供的便捷工具中受益,该工具为您提供了 best base = json.loads(open('base.schema.json').read()) definitions = json.loads(open('definitions.schema.json').read()) schema = json.loads(open('address.schema.json').read()) schema_store = { base.get('$id','base.schema.json') : base, definitions.get('$id','definitions.schema.json') : definitions, schema.get('$id','address.schema.json') : schema, } resolver = RefResolver.from_schema(base, store=schema_store) 模式(根据您的validator_for键):

    $schema
  2. 然后将它们放在一起以实例化Validator = validator_for(base)

    validator

最后,您validator = Validator(schema, resolver=resolver) 您的数据:

validate
  • 将崩溃,因为data = { "shipping_address": { "street_address": "1600 Pennsylvania Avenue NW", "city": "Washington", "state": "DC" }, "billing_address": { "street_address": "1st Street SE", "city": "Washington", "state": 32 } }
"state": 32

更改为>>> validator.validate(data) ValidationError: 32 is not of type 'string' Failed validating 'type' in schema['properties']['billing_address']['properties']['state']: {'type': 'string'} On instance['billing_address']['state']: 32 将进行验证