Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nullable doesn't work on oneOf fields #2450

Closed
person1123 opened this issue Jun 13, 2024 · 4 comments
Closed

Nullable doesn't work on oneOf fields #2450

person1123 opened this issue Jun 13, 2024 · 4 comments
Labels

Comments

@person1123
Copy link

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.

@jasoniangreen
Copy link
Collaborator

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.

@theahura
Copy link

theahura commented Jun 16, 2024

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

@jasoniangreen
Copy link
Collaborator

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

@jasoniangreen
Copy link
Collaborator

Closing as this has been shown to be the expected behaviour, but will explore a change to the docs, by myself or someone else.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

No branches or pull requests

3 participants