Nullable doesn't work on oneOf fields
person1123 opened this issue · 4 comments
What version of Ajv are you using? Does the issue happen if you use the latest version?
8.11.0, Also reproduces on 8.12.0
Ajv options object
{allErrors: true}, {useDefaults: true}
JSON Schema
Schema 1 (doesn't work)
{
"type": "object",
"properties": {
"foo": {
"type": "object",
"oneOf": [
{
"type": "object",
"properties": {
"foo": {
"type": "string"
}
},
"required": [
"foo"
]
},
{
"type": "object",
"properties": {
"bar": {
"type": "number",
"maximum": 3
}
},
"required": [
"bar"
]
}
],
"nullable": true
}
},
"required": [
"foo"
]
}¡
Schema 2 (works)
{
"type": "object",
"properties": {
"foo": {
"type": "object",
"oneOf": [
{
"type": "object",
"properties": {
"foo": {
"type": "string"
}
},
"required": [
"foo"
]
},
{
"type": "object",
"properties": {
"bar": {
"type": "number",
"maximum": 3
}
},
"required": [
"bar"
]
},
{
"type": "null"
}
],
"nullable": true
}
},
"required": [
"foo"
]
}
Sample data
{"foo": null}
Your code
Runkit: https://runkit.com/embed/f2r6epfwc8h7
const Ajv = require("ajv")
const ajv = new Ajv({allErrors: true})
const objectASchema = {
type: "object",
properties: {
foo: {type: "string"},
},
required: ['foo']
}
const objectBSchema = {
type: "object",
properties: {
bar: {type: "number", maximum: 3},
},
required: ['bar']
}
const schema = {
type: "object",
properties: {
foo: {
type: "object",
oneOf: [objectASchema, objectBSchema],
nullable: true
}
},
required: ['foo']
}
const schema2 = {
type: "object",
properties: {
foo: {
type: "object",
oneOf: [objectASchema, objectBSchema, {type: 'null'}],
nullable: true
}
},
required: ['foo']
}
const validate = ajv.compile(schema)
const validate2 = ajv.compile(schema2)
test({foo: {foo: '5'}})
test({foo: {bar: 2}})
test({foo: null})
test2({foo: {foo: '5'}})
test2({foo: {bar: 2}})
test2({foo: null})
function test(data) {
console.log("Testing",data);
const valid = validate(data)
if (valid) console.log("Valid!")
else console.log("Invalid: " + ajv.errorsText(validate.errors))
}
function test2(data) {
console.log("Testing with schema 2",data);
const valid = validate2(data)
if (valid) console.log("Valid!")
else console.log("Invalid: " + ajv.errorsText(validate.errors))
}
Validation result, data AFTER validation, error messages
Testing
Object {foo: Object {foo: "5"}}
Valid!
Testing
Object {foo: Object {bar: 2}}
Valid!
**Testing
Object {foo: null}
Invalid: data/foo must be object, data/foo must be object, data/foo must match exactly one schema in oneOf**
Testing with schema 2
Object {foo: Object {foo: "5"}}
Valid!
Testing with schema 2
Object {foo: Object {bar: 2}}
Valid!
**Testing with schema 2
Object {foo: null}
Valid!**
What results did you expect?
I would expect that since in both schemas the foo
field is marked as nullable, {foo: null}
should pass validation. However, it only passes validation where an explicit null
option is included in the oneOf
Are you going to resolve the issue?
I can use the second version of the schema as a workaround for now.
oneOf
docs are very clear, your data MUST match EXACTLY one of the schemas in the array. In your schema 1 which "doesn't work" the data doesn't match one of the schemas, so for me it appears to be behaving as expected.
I personally think the docs aren't clear at all, and it looks like even within the broader OpenAPI community there was a lot of disagreement about the use of nullable as it relates to json schema.
They did however firmly land in the "nullable doesn't override oneOf" camp.
https://github.com/OAI/OpenAPI-Specification/blob/main/proposals/2019-10-31-Clarify-Nullable.md
It wouldn't be the worst thing to update the ajv docs for oneOf/anyof/allof to mention this very unintuitive interaction pattern
Thanks for the extra info @theahura and for sharing your thoughts. I'm happy to accept an update to the docs. I can do it myself but if someone else submits the PR I can actually approve and merge it whereas if I submit it myself I can't :D
Closing as this has been shown to be the expected behaviour, but will explore a change to the docs, by myself or someone else.