Skip to content

Instantly share code, notes, and snippets.

@eliangcs
Created May 17, 2023 05:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eliangcs/ddff58ddda957905eac2dd85e6bc6a11 to your computer and use it in GitHub Desktop.
Save eliangcs/ddff58ddda957905eac2dd85e6bc6a11 to your computer and use it in GitHub Desktop.
How to ensure a value is JSON encodable (Node.js)
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