JSON

Sometimes, we need to convert an object (an array is an object!) to a plain string, so that its contents can be displayed, compared or copied. In some other times, we want an object to be saved in a file on the disk, stored in the database in the server, transmitted across the network, or used in any other way that requires a string format. JSON.stringify() allows us to serialize an object. To restore the object from the string, use JSON.parse().

JSON stands for JavaScript Object Notation. It is very flexible and can be used to store complex data made up of a deep hierarchy of objects.


var o = {a: 10}, o2 = {x: 5};
o.b = o2;
o.c = [o2, o2];
o.d = new Boolean(true);
o.e = function() { return 9; }; // a function won’t be stored

var s = JSON.stringify(o);
console.log(s);

var ro = JSON.parse(s);
console.log(ro.c[1].x);

{"a":10,"b":{"x":5},"c":[{"x":5},{"x":5}],"d":true} 5

JSON.stringify() may accept a replacer function as its second argument:


var o = {a: 1, b: 'hello', c: {d: 2, e: 'world'}};

function replacer(key, value) {
  return (key === 'd') ? (value * 10)
    : (typeof value === 'string') ? undefined : value; // filtered out completely!
}

console.log(JSON.stringify(o, replacer));

{"a":1,"c":{"d":20}}

The third argument, when passed to JSON.stringify(), denotes the number of spaces used for each indentation level.


var o = {a: 1, b: 'hello', c: {d: 2, e: 'world'}};
console.log(JSON.stringify(o, null, 5)); // pretty-printing JSON string

{ "a": 1, "b": "hello", "c": { "d": 2, "e": "world" } }

For JSON, property names must be double-quoted strings. Circular references, ie. object properties or array elements pointing to the object or array itself, are not allowed. Functions aren't included into the JSON string naturally. However, there is a workaround, by passing to both JSON.stringify() and JSON.parse() a second argument:


var o = {a: x=>x};
console.log(JSON.stringify(o));

function replacer(key, value) {
  return (typeof value === 'function')
    ? "/Function(" + value.toString() + ")/"
    : value;
}

function reviver(key, value) {
  var f = new Function(+value);
  return (typeof value === "string" &&
    value.startsWith("/Function(") && value.endsWith(")/"))
    ? eval("(" + value.substring(10, value.length - 2) + ")")
    : value;
}

var o2 = JSON.parse(JSON.stringify(o, replacer), reviver);
console.log(o2.a(10));

{} 10

(In the past, some established websites such as Google prepend 'while(1);' or tokens like '&&&START&&&' to their private JSON responses to prevent JSON hikacking . Modern browsers have solved this problem inherently. )

Round-tripping numbers through JSON is inherently lossy: "999999999999999999", "999999999999999999.0", and "1000000000000000000" are all distinct source text, yet JSON.parse() collapses them to the very same (imprecise) double-precision number, discarding the original text forever. ECMAScript 2026 addresses this with JSON.parse() source text access: the reviver function passed as JSON.parse()'s second argument now receives a third argument, a context object whose .source property holds the exact, untouched source text of the value currently being revived (only for primitive values, and only when that text differs in a meaningful way).

This is exactly what makes it possible to preserve integers too large for Number without losing precision, by upgrading them to BigInt using the untouched source text rather than the already-rounded parsed value.
const text = '{"id": 999999999999999999, "name": "big"}';

const parsed = JSON.parse(text, (key, value, context) => {
  if (typeof value === 'number' && context && context.source) {
    return BigInt(context.source);   // rebuild from the exact original digits
  }
  return value;
});

console.log(parsed.id);
console.log(JSON.parse(text).id);   // rounded, precision lost

999999999999999999n 1000000000000000000