如何指定 JSON 对象应该取哪一个项目?

How to specify which oneOf item a JSON object should take?

使用 Python 和 jsonschema 我正在尝试验证 ObjAObjB 等对 beta 的分配(test.json )

{
    "alpha": {

        "beta": "ObjA"
    }
}

在我的架构中 (testschema.json) betaoneOf 多个项目,每个项目定义如下(ab, 和 c)

"ObjA": {

    "type": "object",
    "properties": {

        "items": {

            "a": [90, 95],
            "b": [4, 8],
            "c": [0.2, 0.6]
        }
    },

    "additionalProperties": false
}

也就是说,beta可以取oneOf个值,即ObjAObjBObjCObjD。我只是想在 test.json

中指定它应该使用哪一个
"alpha": {

    "type": "object",
    "properties": {

        "beta": {

            "oneOf": [
                {
                    "type": "object",
                    "properties": {

                        "ObjA": {

                            "type": "object",
                            "properties": {

                                "items": {

                                    "a": [90, 95],
                                    "b": [4, 8],
                                    "c": [0.2, 0.6]
                                }
                            },

                            "additionalProperties": false
                        }
                    },

                    "additionalProperties": false
                },

                {
                    "type": "object",
                    "properties": {

                        "ObjB": {

                            "type": "object",
                            "properties": {

                                "items": {

                                    "a": [100],
                                    "b": [0],
                                    "c": [0]
                                }
                            },

                            "additionalProperties": false
                        }
                    }
                },

                ...
                ObjC and ObjD defined
                ...
            }
        }
    }
},

但是,当尝试使用 jsonschema.validate()

验证架构时
### Test the whole JSON is valid against the Schema
def test_valid__JSON_against_schema(self):

    with open(schema_filename) as schema_file:
        test_schema = json.load('testschema.json')
    schema_file.close()

    with open(json_filename) as json_file:
        test_json = json.load('test.json')
    json_file.close()

    validate(test_json, test_schema)

我收到以下错误

Failed validating 'oneOf' in schema['properties']['alpha']['properties']['beta']:

这是完整的消息

E                                                                       
======================================================================  
ERROR: test_valid__JSON_against_schema (__main__.SchemaTests)           
----------------------------------------------------------------------  
Traceback (most recent call last):
  File "test_test-variables.py", line 35, in test_valid__JSON_against_schema
    validate(test_json, test_schema)
  File "/local/tools/PACKAGES/python3/lib/python3.6/site-packages/jsonschema/validators.py", line 541, in validate
    cls(schema, *args, **kwargs).validate(instance)
  File "/local/tools/PACKAGES/python3/lib/python3.6/site-packages/jsonschema/validators.py", line 130, in validate
    raise error
jsonschema.exceptions.ValidationError: 'ObJA' is not valid under any of the given schemas

Failed validating 'oneOf' in schema['properties']['alpha']['properties']['beta']:
    {'oneOf': [{'additionalProperties': False,
                'properties': {'ObjA': {'additionalProperties': False,
                                            'properties': {'items': {'a': [0.2, 0.6],
                                                                     'b': [90, 95],
                                                                     'c': [4, 8]}},
                                            'type': 'object'}},
                'type': 'object'},
               {'additionalProperties': False,
                'properties': {'ObjB': {'additionalProperties': False,
                                            'properties': {'items': {'a': [0],
                                                                     'b': [100],
                                                                     'c': [0]}},
                                            'type': 'object'}},
                'type': 'object'},
               {'additionalProperties': False,
                'properties': {'ObjC': {'additionalProperties': False,
                                                   'properties': {'items': {'a': [0],
                                                                            'b': [50],
                                                                            'c': [50]}},
                                                   'type': 'object'}},
                'type': 'object'},
               {'additionalProperties': False,
                'properties': {'ObjD': {'additionalProperties': False,
                                              'properties': {'items': {'a': [100],
                                                                       'b': [0],
                                                                       'c': [0]}},
                                              'type': 'object'}},
                'type': 'object'}]}

On instance['alpha']['beta']:
    'ObjA'

----------------------------------------------------------------------
Ran 1 test in 0.007s

FAILED (errors=1)

使用在线 jsonschema 验证器 (http://json-schema-validator.herokuapp.com/) test.json 无法验证,因此我从文件中删除了对 alpha 的任何提及(即此 { }) 并且验证器报告了以下内容

[ {
  "level" : "warning",
  "schema" : {
    "loadingURI" : "#",
    "pointer" : "/properties/alpha/properties/beta/oneOf/0/properties/ObjA/properties/items"
  },
  "domain" : "syntax",
  "message" : "the following keywords are unknown and will be ignored: [a, b, c]",
  "ignored" : [ "a", "b", "c" ]
} ]

恢复test.json回来,验证给出

[ {
  "level" : "warning",
  "schema" : {
    "loadingURI" : "#",
    "pointer" : "/properties/alpha/properties/beta/oneOf/0/properties/ObjA/properties/items"
  },
  "domain" : "syntax",
  "message" : "the following keywords are unknown and will be ignored: [a, b, c]",
  "ignored" : [ "a", "b", "c" ]
}, {
  "level" : "warning",
  "schema" : {
    "loadingURI" : "#",
    "pointer" : "/properties/alpha/properties/beta/oneOf/0/properties/ObjA/properties/items"
  },
  "domain" : "syntax",
  "message" : "the following keywords are unknown and will be ignored: [a, b, c]",
  "ignored" : [ "a", "b", "c" ]
}, {
  "level" : "warning",
  "schema" : {
    "loadingURI" : "#",
    "pointer" : "/properties/alpha/properties/beta/oneOf/0/properties/ObjA/properties/items"
  },
  "domain" : "syntax",
  "message" : "the following keywords are unknown and will be ignored: [a, b, c]",
  "ignored" : [ "a", "b", "c" ]
}, {
  "level" : "error",
  "schema" : {
    "loadingURI" : "#",
    "pointer" : "/properties/alpha/properties/beta"
  },
  "instance" : {
    "pointer" : "/alpha/beta"
  },
  "domain" : "validation",
  "keyword" : "oneOf",
  "message" : "instance failed to match exactly one schema (matched 0 out of 1)",
  "matched" : 0,
  "nrSchemas" : 1,
  "reports" : {
    "/properties/alpha/properties/beta/oneOf/0" : [ {
      "level" : "warning",
      "schema" : {
        "loadingURI" : "#",
        "pointer" : "/properties/alpha/properties/beta/oneOf/0/properties/ObjA/properties/items"
      },
      "domain" : "syntax",
      "message" : "the following keywords are unknown and will be ignored: [a, b, c]",
      "ignored" : [ "a", "b", "c" ]
    }, {
      "level" : "error",
      "schema" : {
        "loadingURI" : "#",
        "pointer" : "/properties/alpha/properties/beta/oneOf/0"
      },
      "instance" : {
        "pointer" : "/alpha/beta"
      },
      "domain" : "validation",
      "keyword" : "type",
      "message" : "instance type (string) does not match any allowed primitive type (allowed: [\"object\"])",
      "found" : "string",
      "expected" : [ "object" ]
    } ]
  }
} ]

有谁知道正确的做法吗?

谢谢。

让我们隔离失败的架构部分。

{
  "type": "object",
  "properties": {
    "ObjA": {
      "type": "object",
      "properties": {
        "items": {
          "a": [90, 95],
          "b": [4, 8],
          "c": [0.2, 0.6]
        }
      },
      "additionalProperties": false
    }
  },
  "additionalProperties": false
}

哪个在验证你这部分测试数据

"ObjA"

您看到的错误告诉您测试数据是一个字符串,但模式要求它是一个对象。

与您的模式匹配的测试数据看起来像这样

{
  "ObjA": {
    "items": ???
  }
}

我在这里使用???是因为这里的值可以是任何有效的值JSON。原因是因为此架构不包含任何 JSON 架构关键字。

{
  "a": [90, 95],
  "b": [4, 8],
  "c": [0.2, 0.6]
}

因此,它可以是什么值没有任何限制。您看到的警告消息告诉您 abc 不是关键字。

我不知道你想用这个模式表达什么,但它与你测试数据中的简单字符串相去甚远。

根据评论进行编辑

听起来您正试图将 JSON 架构用于非设计用途的内容。模式应该只描述用户需要关注的内容。您将不得不在另一个步骤中将用户值映射到硬编码结构。无论如何,听起来您需要的是 enum 而不是 oneOf.

{
  "enum": ["ObjA", "ObjB", "ObjC", "ObjD"]
}

{
  "enum": [
    {
      "a": [90, 95],
      "b": [4, 8],
      "c": [0.2, 0.6]
    },
    {
      "a": [100],
      "b": [0],
      "c": [0]
    },
    ...
  ]
}

不可能两者兼得。如果您真的需要在您的架构中表达它,我会添加一个显示映射的自定义关键字。验证器会忽略它(你可能会收到警告),但这种关系会向人类读者和可能的自定义工具表达。它可能看起来像这样

{
  "enum": ["ObjA", "ObjB", "ObjC", "ObjD"]
  "enumValues": {
    "ObjA": {
      "a": [90, 95],
      "b": [4, 8],
      "c": [0.2, 0.6]
    },
    "ObjB": {
      "a": [100],
      "b": [0],
      "c": [0]
    },
    ...
  ]
}

enum 告诉用户哪些值是允许的,验证器可以检查这一点。那么enumValues关键字就是我们为了表达枚举值和它们的实际值之间的关系而编造出来的。