Introduction
Beyond basic stringify/parse, JSON methods accept powerful transformer functions. These let you control exactly how data is serialized and reconstructed—essential for handling dates, filtering sensitive data, and custom object types.
Key Concepts
Replacer: Function or array controlling what's included in stringify output.
Reviver: Function transforming values during parse.
toJSON(): Method objects can implement for custom serialization.
Deep Dive
The Replacer Parameter
javascriptconst user = { name: 'Alice', password: 'secret123', email: 'alice@example.com', age: 30 }; // Array replacer - whitelist properties JSON.stringify(user, ['name', 'email']); // '{"name":"Alice","email":"alice@example.com"}' // Function replacer - transform values JSON.stringify(user, (key, value) => { if (key === 'password') return undefined; // Skip if (typeof value === 'string') return value.toUpperCase(); return value; }); // '{"name":"ALICE","email":"ALICE@EXAMPLE.COM","age":30}'
The Reviver Parameter
javascript// Problem: Dates become strings const json = '{"created":"2024-12-25T00:00:00.000Z"}'; const obj1 = JSON.parse(json); typeof obj1.created; // 'string' - not a Date! // Solution: Use reviver const obj2 = JSON.parse(json, (key, value) => { if (key === 'created') return new Date(value); return value; }); obj2.created instanceof Date; // true! // Generic ISO date reviver const isoDatePattern = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/; function dateReviver(key, value) { if (typeof value === 'string' && isoDatePattern.test(value)) { return new Date(value); } return value; } JSON.parse(json, dateReviver);
Custom toJSON() Method
javascriptclass User { constructor(name, password) { this.name = name; this.password = password; // Sensitive! } // Controls JSON representation toJSON() { return { name: this.name, type: 'User' // password NOT included }; } } const user = new User('Alice', 'secret'); JSON.stringify(user); // '{"name":"Alice","type":"User"}' // Date's built-in toJSON const date = new Date('2024-12-25'); date.toJSON(); // '2024-12-25T00:00:00.000Z'
Deep Clone with JSON
javascript// Quick deep clone (with limitations) const original = { a: 1, b: { c: 2 } }; const clone = JSON.parse(JSON.stringify(original)); clone.b.c = 999; original.b.c; // Still 2! // Limitations: // - Loses undefined, functions, symbols // - Dates become strings // - Doesn't handle circular references // - Slower than structuredClone() // Modern alternative (ES2022+) const betterClone = structuredClone(original);
Handling BigInt
javascript// BigInt throws by default const big = { value: 12345678901234567890n }; // JSON.stringify(big); // TypeError: BigInt can't be serialized // Solution: custom toJSON or replacer JSON.stringify(big, (key, value) => { if (typeof value === 'bigint') return value.toString(); return value; }); // '{"value":"12345678901234567890"}'
Common Pitfalls
- Losing Date types: Always use a reviver for dates.
- toJSON must return JSON-compatible value: Not a string!
- Replacer function called for root too: Key is empty string.
Best Practices
- Implement toJSON for custom classes: Control serialization.
- Use reviver for typed parsing: Reconstruct dates, custom types.
- Use structuredClone() over JSON for cloning: Handles more types.
- Filter sensitive data in toJSON: Don't serialize passwords.
Summary
Replacer filters/transforms during stringify. Reviver transforms during parse. Classes can implement toJSON() for custom serialization. Use revivers to restore dates and custom types. Prefer structuredClone() for deep cloning in modern code.
Code Examples
javascript
const user = {
name: 'Alice',
password: 'secret123',
email: 'alice@example.com',
age: 30
};
// Array replacer - whitelist properties
JSON.stringify(user, ['name', 'email']);
// '{"name":"Alice","email":"alice@example.com"}'
// Function replacer - transform values
JSON.stringify(user, (key, value) => {
if (key === 'password') return undefined; // Skip
if (typeof value === 'string') return value.toUpperCase();
return value;
});
// '{"name":"ALICE","email":"ALICE@EXAMPLE.COM","age":30}'javascript
// Problem: Dates become strings
const json = '{"created":"2024-12-25T00:00:00.000Z"}';
const obj1 = JSON.parse(json);
typeof obj1.created; // 'string' - not a Date!
// Solution: Use reviver
const obj2 = JSON.parse(json, (key, value) => {
if (key === 'created') return new Date(value);
return value;
});
obj2.created instanceof Date; // true!
// Generic ISO date reviver
const isoDatePattern = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/;
function dateReviver(key, value) {
if (typeof value === 'string' && isoDatePattern.test(value)) {
return new Date(value);
}