JavaScript - JSON

Overview

Estimated time: 30–40 minutes

JSON is a text format for data interchange. Learn how to parse and stringify safely, customize serialization, handle dates, convert Map/Set, and avoid common pitfalls.

Learning Objectives

  • Use JSON.parse and JSON.stringify with replacer, reviver, and space parameters.
  • Serialize special types (Date, BigInt, Map/Set) using conventions.
  • Detect/avoid circular references, and understand JSON limitations.
  • Validate and safely consume JSON from untrusted sources.

Prerequisites

Basics

// Stringify with pretty printing
const obj = { a: 1, b: [2, 3] };
JSON.stringify(obj);           // "{"a":1,"b":[2,3]}"
JSON.stringify(obj, null, 2);  // pretty (2 spaces)

// Parse
const s = '{"a":1,"b":[2,3]}';
const data = JSON.parse(s);    // { a:1, b:[2,3] }

Replacer and reviver

// Replacer can filter or transform during stringify
const user = { id: 1, password: 'secret', name: 'Ada' };
JSON.stringify(user, (k, v) => k === 'password' ? undefined : v, 2);
// password omitted

// Reviver can post-process values on parse
const inJson = '{"created":"2025-09-05T12:00:00.000Z"}';
const revived = JSON.parse(inJson, (k, v) => {
  if (typeof v === 'string' && /^\d{4}-\d{2}-\d{2}T/.test(v)) return new Date(v);
  return v;
});
revived.created instanceof Date; // true

Custom toJSON

// Objects can define toJSON to control serialization
class Point {
  constructor(x, y) { this.x = x; this.y = y; }
  toJSON() { return { x: this.x, y: this.y, type: 'Point' }; }
}
JSON.stringify(new Point(1,2)); // "{"x":1,"y":2,"type":"Point"}"

Dates, BigInt, Map/Set

// Dates serialize as ISO strings by default (via Date.prototype.toJSON)
const d = new Date('2025-09-05T12:00:00Z');
JSON.stringify({ d }); // {"d":"2025-09-05T12:00:00.000Z"}

// BigInt is NOT supported by JSON; convert explicitly
const bi = 12345678901234567890n;
JSON.stringify({ bi });           // TypeError
JSON.stringify({ bi: bi.toString() }); // OK: {"bi":"12345678901234567890"}

// Map/Set aren't supported; convert to arrays or objects
const m = new Map([["a",1],["b",2]]);
const s2 = new Set([1,2,3]);
JSON.stringify({ m: Object.fromEntries(m), s: [...s2] });
// {"m":{"a":1,"b":2},"s":[1,2,3]}

Circular references

// JSON.stringify throws on cycles
const a = { name: 'a' };
a.self = a;
// JSON.stringify(a) -> TypeError: Converting circular structure to JSON

// Safe stringify example: mark cycles as "[Circular]"
function safeStringify(value, space = 2) {
  const seen = new WeakSet();
  return JSON.stringify(value, (k, v) => {
    if (typeof v === 'object' && v !== null) {
      if (seen.has(v)) return '[Circular]';
      seen.add(v);
    }
    return v;
  }, space);
}

safeStringify(a); // works

Validation and safety

  • Always use JSON.parse, never eval.
  • Wrap JSON.parse in try/catch for untrusted input.
  • When embedding JSON in HTML, use <script type="application/json"> and parse from .textContent to avoid XSS.

Common Pitfalls

  • Numbers: JSON has finite IEEE-754 numbers. NaN, Infinity, -Infinity serialize as null.
  • Unsupported types: undefined and functions are dropped in objects, become null in arrays.
  • Property order: JSON does not guarantee key order; if you need stability, sort keys before stringifying.
  • BigInt: Not supported—convert to string or number (risking precision loss).

Try it

Run to experiment with replacer, reviver, and special types: