ajv-keywords
ajv-keywords copied to clipboard
Transform (trim) on nested objects via definitions is not working
Hello!
I have found interesting behavior of transform keyword, which is probably a bug.
In short: "transform": [ "trim" ]
is not working on nested objects if specified via definitions.
The schema:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"trimmedString": {
"type": "string",
"transform": ["trim"]
}
},
"type": "object",
"properties": {
"a": {
"$ref": "#/definitions/trimmedString"
},
"nested": {
"type": "object",
"properties": {
"b": {
"$ref": "#/definitions/trimmedString"
},
"c": {
"type": "string",
"transform": ["trim"]
}
}
}
}
}
Input data:
{
"a": " x ",
"nested": {
"b": " y ",
"c": " z "
}
}
Expected results: all strings are trimmed.
Actual results: string b
in nested objects is not trimmed
{
"a": "x",
"nested": {
"b": " y ",
"c": "z"
}
}
My versions:
"ajv": "^7.0.3",
"ajv-formats": "^1.5.1",
"ajv-keywords": "^4.0.0",
Thanks!
you need to activate ajv-keywords with transform as a keyword
you need to activate ajv-keywords with transform as a keyword
Obviously, I did activated "transform" keyword. Without it I get:
Error: strict mode: unknown keyword: "transform"
Here is full example (./schemas/test.json
is exactly as I posted above):
import Ajv from "ajv";
import addKeywords from 'ajv-keywords';
import testSchema from './schemas/test.json';
describe('bug', () => {
it('all strings should be trimmed', () => {
const ajv = new Ajv({ allErrors: true });
addKeywords(ajv, ['transform']);
const validate = ajv.compile(testSchema);
const data = {
a: ' x ',
nested: {
b: ' y ',
c: ' z '
}
};
console.log('Data before validation:', data);
const res = validate(data);
console.log('Data after validation:', data);
expect(res).toBeTrue();
expect(data.a).toEqual('x');
expect(data.nested.b).toEqual('y');
expect(data.nested.c).toEqual('z');
});
});
And the output of the spec is as follows:
Data before validation: { a: ' x ', nested: { b: ' y ', c: ' z ' } }
Data after validation: { a: 'x', nested: { b: ' y ', c: 'z' } }
Failures:
1) bug all strings should be trimmed
Message:
Expected ' y ' to equal 'y'.
As you can see, trim
if specified via custom definitions works for the non-nested field a
, but does not not work for nested field b
.
Field c
is nested, but it is specified without definition.
Hi!
I do have the same issue @Vitaljok is encoutering. I use
"ajv": "^8.6.0",
"ajv-keywords": "^5.0.0",
"ajv-merge-patch": "^5.0.1"
Origin
I have a lot of big schema with nested element or $ref.
Some of my schema repeat themselves: create
schema has multiple required
but not its update
sibling. I use different method to simplify my code but all of them disable trim
- Using $merge to remove my
required
options - Using
lodash.omit
,json-merge-patch
or handmadedeepMerge
Notice
It appears that all schema coming from a function disable trim
. If I console.log
the result of my deepMerge
and paste it in the code, the trim seem to work.
Test:
const Ajv = require('ajv');
const ajv = new Ajv({ coerceTypes: 'array', allowUnionTypes: true });
require('ajv-merge-patch')(ajv);
require("ajv-keywords")(ajv);
const create = {
type: 'object',
properties: {
body: {
type: 'object',
required: ['name'],
properties: {
name: {
type: 'string',
transform: ['trim']
}
}
}
}
};
const update = {
$merge: {
source: create,
with: {
properties: {
body: {
required: []
}
}
}
}
};
const createValidate = ajv.compile(create);
const updateValidate = ajv.compile(update);
const data = {
body: {
name: ' Test string to Trim '
}
};
function _(validate, data) {
try {
validate(data);
console.log({ data });
} catch (e) {
console.log({ e });
}
}
_(updateValidate, data);
_(createValidate, data);
Output:
updateCase = { data: { body: { name: ' Test string to Trim ' } } }
createCase = { data: { body: { name: 'Test string to Trim' } } }
I can send the other methods I used, but ultimately, I am forced to paste my entire schema just to remove required
.