Ad – 728Γ—90
πŸ”§ Intermediate JS

JavaScript JSON – Parsing and Creating JSON Data

JSON (JavaScript Object Notation) is the universal data exchange format of the web. Every API call you make receives JSON; every API call you send likely sends JSON. Understanding how to parse, serialise, transform, and validate JSON is a core skill for any JavaScript developer.

⏱️ 20 min read 🎯 Intermediate πŸ“… Updated 2026

JSON Format Rules

JSON is a strict subset of JavaScript object notation with several important differences.

FeatureJSONJavaScript Object Literal
KeysMust be double-quoted stringsUnquoted identifiers allowed
StringsDouble quotes onlySingle, double, or backtick
FunctionsNot allowedAllowed
undefinedNot allowed (silently dropped)Allowed
CommentsNot allowedAllowed
Trailing commasNot allowedAllowed in modern JS
NaN / InfinityNot allowedAllowed

JSON.parse()

JSON.parse() converts a JSON string into a JavaScript value. It throws a SyntaxError if the string is not valid JSON.

JavaScript
// Basic parsing
const jsonString = '{"name":"Alice","age":28,"hobbies":["coding","reading"]}';
const user = JSON.parse(jsonString);

console.log(user.name);         // 'Alice'
console.log(user.age);          // 28 (number, not string)
console.log(user.hobbies[1]);   // 'reading'

// Parse all JSON types
JSON.parse('"hello"');   // 'hello' (string)
JSON.parse('42');        // 42 (number)
JSON.parse('true');      // true (boolean)
JSON.parse('null');      // null
JSON.parse('[1,2,3]');   // [1, 2, 3] (array)

// Error handling (always wrap in try/catch!)
function safeParse(text, fallback = null) {
  try {
    return JSON.parse(text);
  } catch {
    return fallback;
  }
}

console.log(safeParse('{"a":1}'));       // {a: 1}
console.log(safeParse('not json'));      // null
console.log(safeParse('bad', {}));       // {}

JSON.stringify()

JSON.stringify() converts a JavaScript value to a JSON string. It accepts an optional replacer and a space argument for pretty-printing.

JavaScript
const user = {
  name: 'Alice',
  age: 28,
  password: 'secret123',  // we want to exclude this
  greet() { return 'Hello'; }, // functions are silently dropped
  createdAt: new Date()
};

// Basic serialisation
const json = JSON.stringify(user);
// '{"name":"Alice","age":28,"createdAt":"2026-06-03T..."}'
// Note: password β€” wait, it IS included! See replacer below.
// Note: greet() is dropped (functions not in JSON)

// Pretty-print with 2-space indent
const pretty = JSON.stringify(user, null, 2);
console.log(pretty);
/*
{
  "name": "Alice",
  "age": 28,
  ...
}
*/

// Replacer as array – whitelist of keys to include
const safe = JSON.stringify(user, ['name', 'age'], 2);
// Only name and age are included β€” password excluded!

// Replacer as function – called for each key/value
const filtered = JSON.stringify(user, (key, value) => {
  if (key === 'password') return undefined; // exclude
  if (typeof value === 'number') return value * 2; // transform
  return value;
}, 2);
β–Ά Output
{
  "name": "Alice",
  "age": 28,
  "createdAt": "2026-06-03T12:00:00.000Z"
}
// safe: { "name": "Alice", "age": 28 }
// filtered: { "name": "Alice", "age": 56, "createdAt": "..." }

JSON.parse with Reviver

The reviver function is called for each parsed key/value pair, letting you transform values as JSON is parsed β€” for example, converting date strings back to Date objects.

JavaScript
const data = '{"name":"Alice","birthDate":"1998-03-15T00:00:00.000Z","score":42}';

// Reviver: convert date strings to Date objects
const parsed = JSON.parse(data, (key, value) => {
  // ISO date string pattern
  if (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}T/.test(value)) {
    return new Date(value);
  }
  return value;
});

console.log(parsed.name);      // 'Alice'
console.log(parsed.birthDate); // Date object (not string!)
console.log(parsed.birthDate instanceof Date); // true

// Custom serialisation with toJSON
class Money {
  constructor(amount, currency) {
    this.amount = amount;
    this.currency = currency;
  }
  toJSON() {
    // Custom serialisation format
    return `${this.amount} ${this.currency}`;
  }
}
const price = new Money(29.99, 'USD');
console.log(JSON.stringify({ price })); // '{"price":"29.99 USD"}'

Handling Dates in JSON

JavaScript
// Dates are serialised to ISO 8601 strings by stringify
const event = { title: 'Meeting', date: new Date('2026-06-15') };
const json = JSON.stringify(event);
// '{"title":"Meeting","date":"2026-06-15T00:00:00.000Z"}'

// After parsing, date is a STRING again, not a Date
const parsed = JSON.parse(json);
console.log(typeof parsed.date);     // 'string'
console.log(parsed.date instanceof Date); // false

// Solution 1: reviver (as shown above)
const withDates = JSON.parse(json, (k, v) =>
  typeof v === 'string' && /Z$/.test(v) ? new Date(v) : v
);
console.log(withDates.date instanceof Date); // true

// Solution 2: store timestamps (numbers)
const eventWithTS = { title: 'Meeting', timestamp: Date.now() };
// Numbers survive JSON round-trips perfectly

// Solution 3: use structuredClone for in-memory cloning (preserves Date type)
const clone = structuredClone(event);
console.log(clone.date instanceof Date); // true
πŸ’‘
Date Handling Strategy

When sending dates over an API, ISO 8601 strings are universally understood. When storing dates in memory, use structuredClone() instead of a JSON round-trip β€” it preserves Date objects natively without conversion.

Deep Cloning with JSON

JavaScript
const original = {
  name: 'Alice',
  scores: [95, 82, 88],
  address: { city: 'London' }
};

// JSON round-trip deep clone
const clone = JSON.parse(JSON.stringify(original));
clone.address.city = 'Paris';
clone.scores.push(100);

console.log(original.address.city); // 'London' β€” not affected
console.log(original.scores.length); // 3 β€” not affected

// JSON clone LIMITATIONS:
const problematic = {
  fn: () => 'hello',    // silently dropped
  undef: undefined,      // silently dropped
  date: new Date(),      // converted to ISO string
  regex: /hello/g,       // converted to {}
  map: new Map([[1,2]]), // converted to {}
  nan: NaN,             // converted to null
  inf: Infinity          // converted to null
};
const lostData = JSON.parse(JSON.stringify(problematic));
console.log(lostData);
// { date: '...string...', regex: {}, map: {}, nan: null, inf: null }
// fn and undef are gone

// Prefer structuredClone for reliable deep cloning
const better = structuredClone(original); // handles Date, Set, Map, etc.

Working with API Responses

JavaScript
// Standard fetch + JSON parsing
async function fetchUsers() {
  const response = await fetch('https://jsonplaceholder.typicode.com/users');
  if (!response.ok) {
    throw new Error(`HTTP error: ${response.status}`);
  }
  const users = await response.json(); // response.json() calls JSON.parse internally
  return users;
}

// Sending JSON in a POST request
async function createUser(userData) {
  const response = await fetch('/api/users', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'  // important!
    },
    body: JSON.stringify(userData)
  });
  return response.json();
}

// Safe response parsing with validation
async function safeApiCall(url) {
  try {
    const res = await fetch(url);
    const text = await res.text(); // get raw text first
    let data;
    try {
      data = JSON.parse(text);
    } catch {
      throw new Error(`Server returned non-JSON: ${text.slice(0, 100)}`);
    }
    if (!res.ok) {
      throw new Error(data.message ?? `HTTP ${res.status}`);
    }
    return data;
  } catch (error) {
    console.error('API call failed:', error.message);
    throw error;
  }
}
πŸ’‘
Always Check response.ok

fetch() does not reject on HTTP error status codes (4xx, 5xx) β€” it only rejects on network failures. Always check response.ok (or response.status) before parsing. A server can return a 404 or 500 response with a JSON body, and await response.json() will happily parse it without throwing.

Interview Questions

  • What values does JSON.stringify silently drop?
  • How would you exclude sensitive fields when serialising an object to JSON?
  • Why do Date objects not survive a JSON round-trip?
  • What is the reviver function in JSON.parse?
  • What is the difference between JSON.parse(JSON.stringify(x)) and structuredClone(x)?

πŸ‹οΈ Practical Exercise

Write a serialize(obj, sensitiveKeys = []) function that converts an object to a formatted JSON string (2-space indent), automatically excludes any keys listed in sensitiveKeys, and converts Date objects to human-readable strings using toLocaleString() instead of ISO format. Test it with a user object containing password and createdAt fields.

πŸ”₯ Challenge Exercise

Implement a JSONStorage class that wraps localStorage with automatic JSON serialisation and a schema validator. It should support set(key, value, schema?), get(key, reviver?), and getOrDefault(key, defaultValue). The optional schema validator should be a function that returns true/false β€” if validation fails on set, throw a ValidationError.

Frequently Asked Questions

Why does JSON.stringify(undefined) return undefined (not a string)?
undefined is not a valid JSON value. When the top-level value is undefined, JSON.stringify returns undefined (not the string "undefined"). Inside an object, properties with undefined values are silently omitted. Inside an array, undefined is converted to null.
Can JSON represent circular references?
No. If you pass an object with circular references to JSON.stringify, it throws a TypeError: Converting circular structure to JSON. Use a library like flatted or circular-json, or manually break circular references before serialising.
What is the difference between JSON5 and standard JSON?
JSON5 is a superset of JSON that allows single-quoted strings, comments, trailing commas, unquoted object keys, and Infinity/NaN. It is not natively supported in browsers β€” you need the json5 npm package. Standard JSON is stricter and universally supported.
Is response.json() the same as JSON.parse(await response.text())?
Functionally yes β€” both parse the response body as JSON. response.json() is a convenience method. One subtle difference: response.json() may handle charset detection from response headers, while response.text() gives you the raw decoded string.
How do I validate the structure of a parsed JSON object?
Use a schema validation library like Zod, Yup, or Ajv. These let you define the expected shape and types of your data and throw descriptive errors when the structure doesn't match. This is especially important for API responses where you don't control the server.
Ad – 336Γ—280

πŸ“‹ Summary

  • JSON keys and strings must use double quotes; no comments, trailing commas, or functions.
  • JSON.parse(text) converts a JSON string to a JavaScript value; wrap in try/catch.
  • JSON.stringify(value, replacer, space) converts a value to a JSON string.
  • Use the replacer array to whitelist keys; use a replacer function to filter/transform.
  • Use the reviver to transform values during parsing (e.g., convert date strings to Dates).
  • JSON.stringify silently drops undefined, functions, and Symbols.
  • JSON round-trip cloning loses Date types β€” prefer structuredClone() for in-memory cloning.
  • Always check response.ok after fetch() β€” it does not throw on 4xx/5xx responses.