From 06ba685709dd107978fc15e32d5236d17707a7b2 Mon Sep 17 00:00:00 2001 From: Ofir Stiber Date: Sat, 29 Nov 2025 15:05:44 +0200 Subject: [PATCH] feat(EJSON): add ignoreUndefined option to EJSON serialization - Introduced `ignoreUndefined` option to `EJSON.stringify` to omit undefined values from the output. - Updated tests to cover various scenarios for the new option, including nested objects and arrays. - Ensured compatibility with existing options like `relaxed` and `replacer` functions. --- src/extended_json.ts | 7 +++- test/node/extended_json.test.ts | 64 +++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/src/extended_json.ts b/src/extended_json.ts index 4e2613a9..d8f7e6cd 100644 --- a/src/extended_json.ts +++ b/src/extended_json.ts @@ -39,6 +39,11 @@ export type EJSONOptions = { * @defaultValue `false` */ useBigInt64?: boolean; + /** + * Omits undefined values from the output instead of converting them to null + * @defaultValue `false` + */ + ignoreUndefined?: boolean; }; /** @internal */ @@ -242,7 +247,7 @@ function serializeValue(value: any, options: EJSONSerializeOptions): any { if (Array.isArray(value)) return serializeArray(value, options); - if (value === undefined) return null; + if (value === undefined) return options.ignoreUndefined ? undefined : null; if (value instanceof Date || isDate(value)) { const dateNum = value.getTime(), diff --git a/test/node/extended_json.test.ts b/test/node/extended_json.test.ts index 2ce482b6..44b4dcd1 100644 --- a/test/node/extended_json.test.ts +++ b/test/node/extended_json.test.ts @@ -574,6 +574,70 @@ describe('Extended JSON', function () { expect(result).to.deep.equal({ a: 1 }); }); + describe('ignoreUndefined option', () => { + it('should convert undefined to null by default', () => { + const doc = { a: 1, b: undefined, c: 'test' }; + const serialized = EJSON.stringify(doc); + expect(serialized).to.equal('{"a":1,"b":null,"c":"test"}'); + }); + + it('should omit undefined values when ignoreUndefined is true', () => { + const doc = { a: 1, b: undefined, c: 'test' }; + const serialized = EJSON.stringify(doc, { ignoreUndefined: true }); + expect(serialized).to.equal('{"a":1,"c":"test"}'); + }); + + it('should handle nested undefined values with ignoreUndefined: true', () => { + const doc = { a: 1, nested: { b: undefined, c: 2 }, d: 'test' }; + const serialized = EJSON.stringify(doc, { ignoreUndefined: true }); + expect(serialized).to.equal('{"a":1,"nested":{"c":2},"d":"test"}'); + }); + + it('should handle nested undefined values without ignoreUndefined (default behavior)', () => { + const doc = { a: 1, nested: { b: undefined, c: 2 }, d: 'test' }; + const serialized = EJSON.stringify(doc); + expect(serialized).to.equal('{"a":1,"nested":{"b":null,"c":2},"d":"test"}'); + }); + + it('should handle undefined in arrays with ignoreUndefined: true', () => { + const doc = { arr: [1, undefined, 3] }; + const serialized = EJSON.stringify(doc, { ignoreUndefined: true }); + // JSON.stringify converts undefined array elements to null + expect(serialized).to.equal('{"arr":[1,null,3]}'); + }); + + it('should handle undefined in arrays without ignoreUndefined (default behavior)', () => { + const doc = { arr: [1, undefined, 3] }; + const serialized = EJSON.stringify(doc); + expect(serialized).to.equal('{"arr":[1,null,3]}'); + }); + + it('should handle object with all undefined values with ignoreUndefined: true', () => { + const doc = { a: undefined, b: undefined }; + const serialized = EJSON.stringify(doc, { ignoreUndefined: true }); + expect(serialized).to.equal('{}'); + }); + + it('should work with other options like relaxed', () => { + const doc = { a: new Int32(10), b: undefined, c: new Double(3.14) }; + const serialized = EJSON.stringify(doc, { ignoreUndefined: true, relaxed: false }); + expect(serialized).to.equal('{"a":{"$numberInt":"10"},"c":{"$numberDouble":"3.14"}}'); + }); + + it('should work with replacer function', () => { + const doc = { a: 1, b: undefined, c: 2 }; + const replacer = (key: string, value: unknown) => (key === 'a' ? 100 : value); + const serialized = EJSON.stringify(doc, replacer, 0, { ignoreUndefined: true }); + expect(serialized).to.equal('{"a":100,"c":2}'); + }); + + it('should work with space parameter', () => { + const doc = { a: 1, b: undefined }; + const serialized = EJSON.stringify(doc, undefined, 2, { ignoreUndefined: true }); + expect(serialized).to.equal('{\n "a": 1\n}'); + }); + }); + it(`throws if Symbol.for('@@mdb.bson.version') is the wrong version in EJSON.stringify`, () => { expect(() => EJSON.stringify({