JSON.stringify
- JSON-encoded object has several important differences from the object literal:
- Strings use double quotes.
- Object property names are double-quoted.
- JSON is data-only cross-language specification, so some JavaScript-specific object properties are skipped by
JSON.stringify
:
- Function properties (methods).
- Symbolic properties.
- Properties that store
undefined
.
- Nested objects are supported and converted automatically.
- The important limitation is that there must be no circular references.
JSON.stringify({}); // '{}'
JSON.stringify(true); // 'true'
JSON.stringify('foo'); // '"foo"'
JSON.stringify([1, 'false', false]); // '[1,"false",false]'
JSON.stringify({ x: 5 }); // '{"x":5}'
JSON.stringify(new Date(2006, 0, 2, 15, 4, 5)) // '"2006-01-02T15:04:05.000Z"'
JSON.stringify({ x: 5, y: 6 }); // '{"x":5,"y":6}' or '{"y":6,"x":5}'
JSON.stringify([new Number(1), new String('false'), new Boolean(false)]); // '[1,"false",false]'
JSON.stringify({ x: [10, undefined, function(){}, Symbol('')] }); // '{"x":[10,null,null,null]}'
// Symbols:
JSON.stringify({ x: undefined, y: Object, z: Symbol('') }); // '{}'
JSON.stringify({ [Symbol('foo')]: 'foo' }); // '{}'
JSON.stringify({ [Symbol.for('foo')]: 'foo' }, [Symbol.for('foo')]); // '{}'
JSON.stringify({ [Symbol.for('foo')]: 'foo' }, function(k, v) {
if (typeof k === 'symbol') {
return 'a symbol';
}
}); // '{}'
// Non-enumerable properties:
JSON.stringify(Object.create(null, {
x: {
value: 'x',
enumerable: false
},
y: {
value: 'y',
enumerable: true
}
})); // '{"y":"y"}'
- The full syntax of
JSON.stringify
is includes two extra parameters:
replacer
is an array of properties to encode or a mapping function function(key, value).
space
is an amount of space to use for formatting
let room = {
number: 23
};
let meetup = {
title: "Conference",
participants: [{name: "John"}, {name: "Alice"}],
place: room // meetup references room
};
room.occupiedBy = meetup; // room references meetup
console.log(JSON.stringify(meetup, ['title', 'participants'])); // {"title":"Conference","participants":[{},{}]}
- Here we are probably too strict. The property list is applied to the whole object structure.
- So participants are empty, because
name
is not in the list.
- Let’s include every property except
room.occupiedBy
that would cause the circular reference:
let room = {
number: 23
};
let meetup = {
title: "Conference",
participants: [{name: "John"}, {name: "Alice"}],
place: room // meetup references room
};
room.occupiedBy = meetup; // room references meetup
alert(JSON.stringify(meetup, ['title', 'participants', 'place', 'name', 'number']));
/*
{
"title":"Conference",
"participants":[{"name":"John"},{"name":"Alice"}],
"place":{"number":23}
}
*/
- Now everything except
occupiedBy
is serialized. But the list of properties is quite long.
- Fortunately, we can use a function instead of an array as the
replacer
.
- The function will be called for every
(key, value)
pair and should return the “replaced” value, which will be used instead of the original one.
- In our case, we can return
value
“as is” for everything except occupiedBy
.
- To ignore
occupiedBy
, the code below returns undefined
:
let room = {
number: 23
};
let meetup = {
title: "Conference",
participants: [{name: "John"}, {name: "Alice"}],
place: room // meetup references room
};
room.occupiedBy = meetup; // room references meetup
alert( JSON.stringify(meetup, function replacer(key, value) {
alert(`${key}: ${value}`); // to see what replacer gets
return (key == 'occupiedBy') ? undefined : value;
}));
/* key:value pairs that come to replacer:
: [object Object]
title: Conference
participants: [object Object],[object Object]
0: [object Object]
name: John
1: [object Object]
name: Alice
place: [object Object]
number: 23
*/
- Please note that
replacer
function gets every key/value pair including nested objects and array items. It is applied recursively. The value of this
inside replacer
is the object that contains the current property.
- The first call is special. It is made using a special “wrapper object”:
{"": meetup}
. In other words, the first (key, value)
pair has an empty key, and the value is the target object as a whole. That’s why the first line is ":[object Object]"
in the example above.
- The idea is to provide as much power for
replacer
as possible: it has a chance to analyze and replace/skip the whole object if necessary.