oneOf support when building objects
Closed this issue · 2 comments
oneOf
statements are supported when deserialising from JSON, but not when constructing an object. The library appears to assume the first option when following a oneOf
branch, and does not check other branches when a validation failure occurs.
Schema
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Schema",
"type": "object",
"additionalProperties": false,
"properties": {
"type": {
"type": "string"
},
"data": {
"type": "object"
}
},
"oneOf": [{
"properties": {
"type": {
"enum": ["A"]
},
"data": {
"properties": {
"bar": {
"type": "string"
}
}
}
}
},
{
"properties": {
"type": {
"enum": ["B"]
},
"data": {
"properties": {
"bar": {
"type": "number"
}
}
}
}
}
]
}
Python
import python_jsonschema_objects as jso
builder = jso.ObjectBuilder('schema.json')
namespace = builder.build_classes()
Schema = namespace.Schema
instance = Schema.from_json("""
{
"type": "B",
"data": {
"bar": 1
}
}
""")
instance.validate() # success
instance2 = Schema()
instance2.type = "B" # failure
instance2.data = {
"bar": 1
}
Stacktrace
Traceback (most recent call last):
File "schema.py", line 19, in <module>
instance2.type = "B"
File "/home/user/.local/lib/python3.6/site-packages/python_jsonschema_objects/classbuilder.py", line 225, in __setattr__
prop.__set__(self, val)
File "/home/user/.local/lib/python3.6/site-packages/python_jsonschema_objects/descriptors.py", line 99, in __set__
validator = info["type"](val)
File "/home/user/.local/lib/python3.6/site-packages/python_jsonschema_objects/literals.py", line 44, in __init__
self.validate()
File "/home/user/.local/lib/python3.6/site-packages/python_jsonschema_objects/literals.py", line 84, in validate
validator(paramval, self._value, info)
File "/home/user/.local/lib/python3.6/site-packages/python_jsonschema_objects/validators.py", line 51, in enum
raise ValidationError("{0} is not one of {1}".format(value, param))
python_jsonschema_objects.validators.ValidationError: B is not one of ['A']
Curiously if you provide the property which causes the branch during initialisation, it succeeds.
instance2 = Schema(type="B") # success
instance2.data = {
"bar": 1
}
This is unavoidable because when we materialize an object or property that could be one of multiple types, we need to choose a type. The builder can deal with the uncertainty, but the actual object can't.
In the first case, you're not providing a discriminating piece of information, so it's just going to choose the first one that is valid (which boils down to the first one in the list). In the second one, A
isn't valid, so it makes it of type B
.