1

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.

Dodomac
  • 194
  • 2
  • 17
  • Almost impossible to help without knowing what the JSON instance you are trying to validate looks like. Because you are generating it dynamically it could be anything. – tom redfern Jul 24 '17 at 15:06
  • Start of text - "beta": "ObjA" - I have edited the sentence and JSON so it is clearer. – Dodomac Jul 24 '17 at 15:11
  • Yes, that is exactly the problem. You're getting a validation error when trying to validate "something" against a schema. The "something" you are trying to validate is failing validation. Unfortunately, it's impossible for us to see *why* it's failing (and therefore be able to offer you some help) because we don't know what it looks like. – tom redfern Jul 24 '17 at 15:25

1 Answers1

4

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.

Jason Desrosiers
  • 22,479
  • 5
  • 47
  • 53
  • Values a, b, and c are fixed arrays for ObjA, ObjB, ObjC and ObjD. I would simply like to assign the whole of ObjA to beta without specifying it's fixed contents, or in other words I am trying to assign a JSON object with multiple arrays by assigning the object only. I will add this information to the original question. – Dodomac Jul 25 '17 at 07:53
  • +1 for identifying the problem. Still need a way to correctly specify an object with multiple arrays of set values (a, b and c). Itself in an object (ObjA) that is a choice to be selected. – Dodomac Jul 25 '17 at 09:48
  • Maybe the problem is that I am trying to specify values in the schema (which you allude to here https://stackoverflow.com/a/30465935/1413785). The reason I do this is because I do not want the user to specify these values as they are set values for a type of test they want to perform. – Dodomac Jul 25 '17 at 10:34
  • I have reformatted the JSON and reworded the question here; https://stackoverflow.com/questions/45307399/how-to-use-json-schema-oneof-for-arrays-with-fixed-values – Dodomac Jul 25 '17 at 15:20
  • Thanks for the great explanation. Shame this feature isn't available. – Dodomac Jul 25 '17 at 21:21