Created
May 17, 2023 05:25
-
-
Save eliangcs/ddff58ddda957905eac2dd85e6bc6a11 to your computer and use it in GitHub Desktop.
How to ensure a value is JSON encodable (Node.js)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const assert = require('assert'); | |
const _ = require('lodash'); | |
const ensureJSONEncodable = (obj, path = null, visited = null) => { | |
if (obj === null || _.isBoolean(obj) || _.isNumber(obj) || _.isString(obj)) { | |
return; | |
} | |
path = path || []; | |
if (!_.isPlainObject(obj) && !_.isArray(obj)) { | |
const typeName = typeof obj; | |
const pathStr = path.join('.'); | |
throw new TypeError( | |
`Type '${typeName}' is not JSON-encodable (path: '${pathStr}')` | |
); | |
} | |
visited = visited || new Set(); | |
if (visited.has(obj)) { | |
const pathStr = path.join('.'); | |
throw new TypeError( | |
`Circular structure is not JSON-encodable (path: '${pathStr}')` | |
); | |
} | |
visited.add(obj); | |
for (const key in obj) { | |
ensureJSONEncodable(obj[key], path.concat(key), visited); | |
} | |
}; | |
const test = () => { | |
ensureJSONEncodable(null); | |
ensureJSONEncodable(1.23456); | |
ensureJSONEncodable('hello'); | |
ensureJSONEncodable({ foo: 'bar' }); | |
ensureJSONEncodable({ foo: { bar: 'baz' } }); | |
ensureJSONEncodable({ foo: { bar: 'baz', items: [null, 1234, {}] } }); | |
// These object keys are stringified by JSON.stringify(), e.g., 12.34 -> | |
// '12.34', so I guess we can consider them JSON-encodable. | |
ensureJSONEncodable({ 12.34: 56.78 }); | |
ensureJSONEncodable({ [null]: true }); | |
ensureJSONEncodable({ [undefined]: true }); | |
assert.throws( | |
() => { | |
ensureJSONEncodable(undefined); | |
}, | |
{ | |
name: 'TypeError', | |
message: "Type 'undefined' is not JSON-encodable (path: '')", | |
} | |
); | |
assert.throws( | |
() => { | |
ensureJSONEncodable(/hello/); | |
}, | |
{ | |
name: 'TypeError', | |
message: "Type 'object' is not JSON-encodable (path: '')", | |
} | |
); | |
assert.throws( | |
() => { | |
ensureJSONEncodable(console); | |
}, | |
{ | |
name: 'TypeError', | |
message: "Type 'object' is not JSON-encodable (path: '')", | |
} | |
); | |
assert.throws( | |
() => { | |
ensureJSONEncodable({ foo: [null, { xyz: undefined }] }); | |
}, | |
{ | |
name: 'TypeError', | |
message: "Type 'undefined' is not JSON-encodable (path: 'foo.1.xyz')", | |
} | |
); | |
assert.throws( | |
() => { | |
ensureJSONEncodable(function () {}); | |
}, | |
{ | |
name: 'TypeError', | |
message: "Type 'function' is not JSON-encodable (path: '')", | |
} | |
); | |
assert.throws( | |
() => { | |
ensureJSONEncodable({ foo: [null, { hello: function () {} }] }); | |
}, | |
{ | |
name: 'TypeError', | |
message: "Type 'function' is not JSON-encodable (path: 'foo.1.hello')", | |
} | |
); | |
assert.throws( | |
() => { | |
const obj = { num: 1 }; | |
obj.items = [{ self: obj }]; | |
ensureJSONEncodable(obj); | |
}, | |
{ | |
name: 'TypeError', | |
message: | |
"Circular structure is not JSON-encodable (path: 'items.0.self')", | |
} | |
); | |
}; | |
test(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment