diff --git a/README.md b/README.md index f3a0d206..6a574472 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,7 @@ const stringify = fastJson(mySchema, { - `rounding`: setup how the `integer` types will be rounded when not integers. [More details](#integer) - `largeArrayMechanism`: set the mechanism that should be used to handle large (by default `20000` or more items) arrays. [More details](#largearrays) +- `stripNull`: removes keys with null values from serialised output. If you have lots of null values, using this will result in smallerpayload and improved performance. diff --git a/index.js b/index.js index 862c7c83..6bc0c744 100644 --- a/index.js +++ b/index.js @@ -374,7 +374,7 @@ function buildInnerObject (context, location) { code += ` value = obj[${sanitizedKey}] - if (value !== undefined) { + if ${context.options.stripNull ? '(value != null)' : '(value !== undefined)'} { ${addComma} json += ${JSON.stringify(sanitizedKey + ':')} ${buildValue(context, propertyLocation, 'value')} diff --git a/test/stripNull.test.js b/test/stripNull.test.js new file mode 100644 index 00000000..c2eedea5 --- /dev/null +++ b/test/stripNull.test.js @@ -0,0 +1,110 @@ +const test = require('tap').test +const build = require('..') + +test('should handle null string values properly', (t) => { + t.plan(3) + + const schema = { + type: 'object', + properties: { + firstName: { type: 'string' }, + lastName: { type: 'string' } + }, + required: ['firstName'] + } + const stringify = build(schema, { stripNull: true }) + + const json1 = { firstName: 'Matteo', lastName: 'Collina' } + t.equal(stringify(json1), '{"firstName":"Matteo","lastName":"Collina"}') + + const json2 = { firstName: 'Matteo', lastName: null } + t.equal(stringify(json2), '{"firstName":"Matteo"}') + + const json3 = { firstName: null, lastName: 'Collina' } + t.throws(() => stringify(json3)) // throw error when required property is missing +}) + +test('should handle null int values properly', (t) => { + t.plan(3) + + const schema = { + type: 'object', + properties: { + name: { type: 'string' }, + age: { type: 'integer' }, + experience: { type: 'integer' } + } + } + const stringify = build(schema, { stripNull: true }) + + const json1 = { name: 'Matteo', age: 32, experience: 12 } + t.equal(stringify(json1), '{"name":"Matteo","age":32,"experience":12}') + + const json2 = { name: 'Matteo', age: null, experience: null } + t.equal(stringify(json2), '{"name":"Matteo"}') + + const json3 = { name: 'Matteo', age: 3, experience: null } + t.equal(stringify(json3), '{"name":"Matteo","age":3}') +}) + +test('should handle arrays properly', (t) => { + t.plan(1) + + const schema = { + type: 'object', + properties: { + name: { type: 'string' }, + experience: { + type: 'array', + items: { + type: 'object', + properties: { + company: { type: 'string' }, + years: { type: 'integer' } + } + } + } + } + } + const stringify = build(schema, { stripNull: true }) + + const json1 = { + name: 'Matteo', + experience: [ + { company: 'C1', years: 12 }, + { company: 'C2', years: null }, + { company: null, years: 6 }, + { company: null, years: null } + ] + } + + t.equal( + stringify(json1), + '{"name":"Matteo","experience":[{"company":"C1","years":12},{"company":"C2"},{"years":6},{}]}' + ) +}) + +test('should handle objects and nested objects properly', (t) => { + t.plan(2) + + const schema = { + type: 'object', + properties: { + name: { type: 'string' }, + address: { + type: 'object', + properties: { + city: { type: 'string' }, + country: { type: 'string' } + } + } + } + } + const stringify = build(schema, { stripNull: true }) + + const json1 = { name: 'Matteo', address: { city: 'Mumbai', country: null } } + t.equal(stringify(json1), '{"name":"Matteo","address":{"city":"Mumbai"}}') + + const json2 = { name: null, address: { city: null, country: 'India' } } + t.equal(stringify(json2), '{"address":{"country":"India"}}') +}) diff --git a/types/index.d.ts b/types/index.d.ts index 5ded284d..6dab922f 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -187,6 +187,13 @@ declare namespace build { * @default 'default' */ largeArrayMechanism?: 'default' | 'json-stringify' + + /** + * Specify if null key-value are removed from serialised JSON output, for a smaller payload + * + * @default 'false' + */ + stripNull?: boolean } export const validLargeArrayMechanisms: string[]