Home > Mobile >  AJV JsonSchema validator reports seemingly incorrect error
AJV JsonSchema validator reports seemingly incorrect error

Time:11-28

I'm using AJV to validate a HTTP request payload against a schema. However, I see an error reported that I was not expecting. This is a code example to demonstrate the issue:

const schema = {
  type: 'array',
  minItems: 1,
  items: {
    anyOf: [
      {
        type: 'object',
        properties: {
          op: {
            enum: ['replace']
          },
          path: {
            type: 'string',
            pattern: '/data/to/foo/bar',
          },
          value: {
            type: 'string',
          },
        },
      },{
        type: 'object',
        properties: {
          op: {
            enum: ['replace']
          },
          path: {
            type: 'string',
            pattern: '/data/to/baz',
          },
          value: {
            type: 'object',
            required: ['foo', 'bar'],
            properties: {
              foo: {
                type: 'string',
              },
              bar: {
                type: 'string',
              },
            }
          }
        }
      }
    ],
  },
}

const validator = new ajv()

const compiledValidator = validator.compile(schema)
  const data = [
  { // this object should pass
    op: 'replace',
    path: '/data/to/foo/bar',
    value: 'foo',
  },
  { // this object should fail in the `value` mismatch (missing required attribute)
    op: 'replace',
    path: '/data/to/baz',
    value: {
      foo: 'bar',
    },
  },
]

compiledValidator(data)

console.log(compiledValidator.errors)

The schema defines a number of objects to which an incoming list of data objects should match. The first data item matches the schema (first item schema), however the second data item misses a required attribute (bar) in the value object.

When I run the above code I get the following output:

[
  {
    instancePath: '/1/path',
    schemaPath: '#/items/anyOf/0/properties/path/pattern',
    keyword: 'pattern',
    params: { pattern: '/data/to/foo/bar' },
    message: 'must match pattern "/data/to/foo/bar"'
  },
  {
    instancePath: '/1/value',
    schemaPath: '#/items/anyOf/1/properties/value/required',
    keyword: 'required',
    params: { missingProperty: 'bar' },
    message: "must have required property 'bar'"
  },
  {
    instancePath: '/1',
    schemaPath: '#/items/anyOf',
    keyword: 'anyOf',
    params: {},
    message: 'must match a schema in anyOf'
  }
]

I understand the 2nd and the 3rd (last) errors. However, The first error seems to indicate that the path doesn't match path requirements of the first item schema. It is true that the 2nd data item doesn't match the 1st schema item but I don't seem to understand how it is relevant. I would assume that the error would be focused around the value, not the path since it matches on the path schemas.

Is there a way to get the error reporting more focused around the errors that matter?

CodePudding user response:

There is no way for the evaluator to know whether you intended the first "anyOf" subschema to match or the second, so the most useful thing to do is to show you all the errors.

It can be confusing because you don't need to resolve all the errors, just some of them, which is why some implementations also offer a heirarchical error format to make it more easy to see relationships like this. Maybe if you request that ajv implement more of these error formats, it will happen :)

CodePudding user response:

You can see that all of the errors pertain to the second item in the data by looking at the instancePath for each error, which all start with /1. This is the location within the data that generated the error.

So let's look at the second item and compare it against the schema.

{ // this object should fail in the `value` mismatch (missing required attribute)
  op: 'replace',
  path: '/data/to/baz',
  value: {
    foo: 'bar',
  },
}

The schema says that an item should either (from the anyOf) have

  • path: '/data/to/foo/bar' and value: { type: 'string' }, or
  • path: '/data/to/baz' and value: { required: [ 'foo', 'bar' ] }

The reported errors are:

  1. The first case fails because path is wrong.
  2. The second case fails because /value/bar is not present.
  3. The last error is just the anyOf reporting that none of the options passed.
  • Related