[英]How to set up local file references in python-jsonschema document?
我有一组符合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机器上可能很重要。
我最难弄清楚如何解决一组相互$ref
的模式(我是 JSON 模式的新手)。 事实证明,关键是创建一个带有store
的RefResolver
,该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
构建的。
您必须为每个使用相对引用的模式构建一个自定义的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)
跟进@chris-w 提供的答案,我想用jsonschema 3.2.0
做同样的事情,但他的答案并没有完全涵盖它我希望这个答案能帮助那些仍然来寻求帮助但正在使用这个问题的人包的更新版本。
要使用库扩展 JSON 模式,请执行以下操作:
base.schema.json
{
"$id": "base.schema.json",
"type": "object",
"properties": {
"prop": {
"type": "string"
}
},
"required": ["prop"]
}
extend.schema.json
{
"allOf": [
{"$ref": "base.schema.json"},
{
"properties": {
"extra": {
"type": "boolean"
}
},
"required": ["extra"]
}
]
}
data.json
{
"prop": "This is the property",
"extra": true
}
#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。 这应该适用于单级引用(扩展基础的子项),您需要小心您的架构以及如何设置RefResolver
和Validator
对象。
PS 这是一个练习上述内容的片段。 尝试修改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)
修复了对
base
架构的错误引用 ($ref
)。 更新的例子使用从文档中的一个: https://json-schema.org/understanding-json-schema/structuring.html
正如评论中所指出的,在下面我使用了以下导入:
from jsonschema import validate, RefResolver from jsonschema.validators import validator_for
这只是@Daniel 答案的另一个版本——这对我来说是正确的。 基本上,我决定在基本架构中定义$schema
schema。 然后释放其他模式并在实例化解析器时进行明确调用。
RefResolver.from_schema()
获得(1)一些模式和(2)模式存储的事实对我来说不是很清楚订单和哪些“一些”模式在这里相关。 所以你在下面看到的结构。我有以下几点:
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()
的更清晰的调用:
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
键):
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
将其更改为
"DC"
,并将验证.
我的方法是将所有架构片段预加载到 RefResolver 缓存。 我创建了一个说明这一点的要点: https : //gist.github.com/mrtj/d59812a981da17fbaa67b7de98ac3d4b
这就是我用来从给定目录中的所有模式动态生成schema_store
的方法
base.schema.json
{
"$id": "base.schema.json",
"type": "object",
"properties": {
"prop": {
"type": "string"
}
},
"required": ["prop"]
}
extend.schema.json
{
"$id": "extend.schema.json",
"allOf": [
{"$ref": "base.schema.json"},
{
"properties": {
"extra": {
"type": "boolean"
}
},
"required": ["extra"]
}
]
}
instance.json
{
"prop": "This is the property string",
"extra": true
}
validator.py
import json
from pathlib import Path
from jsonschema import Draft7Validator, RefResolver
from jsonschema.exceptions import RefResolutionError
schemas = (json.load(open(source)) for source in Path("schema/dir").iterdir())
schema_store = {schema["$id"]: schema for schema in schemas}
schema = json.load(open("schema/dir/name.schema.json"))
instance = json.load(open("instance/dir/instance.json"))
resolver = RefResolver.from_schema(schema, store=schema_store)
validator = Draft7Validator(schema, resolver=resolver)
try:
errors = sorted(validator.iter_errors(instance), key=lambda e: e.path)
except RefResolutionError as e:
print(e)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.