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

不问归期 提交于 2019-12-02 09:54:18

问题


Using Python and jsonschema I am trying to validate the assignment of ObjA or ObjB etc. to beta (test.json)

{
    "alpha": {

        "beta": "ObjA"
    }
}

In my schema (testschema.json) beta is oneOf a number of items and each item is defined as below (with differing values for a, b, and c)

"ObjA": {

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

        "items": {

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

    "additionalProperties": false
}

That is to say, beta can take on oneOf values that are ObjA, ObjB, ObjC and ObjD. I am simply trying to specify which one it should use in 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
                ...
            }
        }
    }
},

However, when trying to validate against the schema using 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)

I get the following error

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

Here is the whole message

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)

Using the online jsonschema validator (http://json-schema-validator.herokuapp.com/) test.json does not validate, so I removed any mention of alpha from the file (i.e. to this { }) and the validator reported the following

[ {
  "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" ]
} ]

Restoring test.json back, the validation gives

[ {
  "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" ]
    } ]
  }
} ]

Does anyone know the correct way of doing this?

Thanks.


回答1:


Let's isolate the part of the schema that is failing.

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

Which is valiating this part of you test data

"ObjA"

The error you are seeing is telling you that the test data is a string, but the schema requires that it be an object.

Test data that matches your schema would look something like this

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

I use ??? here because the value here can be any value that is valid JSON. The reason is because this schema does not contain any JSON Schema keywords.

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

Therefore, there are no constraints on what value it can be. The warning messages you are seeing are telling you that a, b and c are not keywords.

I don't know what you are trying to express with this schema, but it's a far cry from the simple string in your test data.

Edit in response to comments

It sounds like you are trying to use JSON Schema for something it isn't designed for. The schema should describe only what the user needs to be concerned about. You will have to map the user values to hard coded structure in another step. In any case, it sounds like what you need is enum rather than oneOf.

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

or

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

There is no way to have both. If you really need to express that in your schema, I would add a custom keyword that shows the mapping. A validator will ignore it (and you might get a warning), but the relationship will be expressed for human readers and perhaps custom tools. It might look something like this

{
  "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]
    },
    ...
  ]
}

The enum tells the user what values are allowed and the validator can check this. Then the enumValues keyword is something we made up to express the relationship between the enum values and their actual values.



来源:https://stackoverflow.com/questions/45281533/how-to-specify-which-oneof-item-a-json-object-should-take

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!