jsguides

JSON.parse()

JSON.parse(text, reviver)

JSON.parse() converts a JSON-formatted string into a JavaScript value. It is the standard way to deserialize data received from APIs, files, or localStorage. The method handles all valid JSON types: objects, arrays, strings, numbers, booleans, and null.

Syntax

JSON.parse(text)
JSON.parse(text, reviver)

Parameters

ParameterTypeDefaultDescription
textstringThe JSON string to parse. Must conform to valid JSON syntax.
reviverfunctionundefinedOptional function that transforms each value before returning it. Called with (key, value, context) for each property.

The reviver Function

When provided, the reviver function receives three arguments:

  • key: The property name (string), even for array indices
  • value: The parsed value
  • context: An object containing source — the original JSON text for primitive values

The reviver runs in a depth-first manner, processing nested values before their parents. If the reviver returns undefined, the property is removed from the result.

Examples

Parsing basic values

// Parsing an object
const data = JSON.parse('{"name": "Alice", "age": 30}');
console.log(data.name); // "Alice"
console.log(data.age);  // 30

// Parsing different JSON types
console.log(JSON.parse("{}"));              // {}
console.log(JSON.parse("true"));            // true
console.log(JSON.parse('"hello"'));         // "hello"
console.log(JSON.parse("[1, 2, 3]"));       // [1, 2, 3]
console.log(JSON.parse("null"));            // null

The examples above parse values as-is. When you need to transform values during parsing—converting date strings to Date objects or doubling numeric values—the reviver function gives you per-key control. It runs depth-first and receives the key, parsed value, and a context object with the raw source text.

Using the reviver parameter

// Double all numbers during parsing
const result = JSON.parse(
  '{"a": 1, "b": 2, "c": 3}',
  (key, value) => {
    if (typeof value === "number") {
      return value * 2;
    }
    return value;
  }
);
console.log(result); // { a: 2, b: 4, c: 6 }

// Parsing dates stored as strings
const withDates = JSON.parse(
  '{"created": "2024-01-15T10:30:00Z"}',
  (key, value) => {
    if (key === "created") {
      return new Date(value);
    }
    return value;
  }
);
console.log(withDates.created); // 2024-01-15T10:30:00.000Z (Date object)

The date-reviver pattern works well for API responses that store timestamps as strings. A related problem with JSON parsing is number precision: JSON numbers become 64-bit floats in JavaScript, losing exactness beyond 2^53 − 1. The reviver’s third argument, context.source, exposes the original raw string so you can recover large integers as BigInt.

Handling large numbers without precision loss

// JSON numbers lose precision beyond Number.MAX_SAFE_INTEGER
// Store as string, revive to BigInt
const bigNum = JSON.parse(
  '{"id": 9007199254740993}',
  (key, value, context) => {
    if (key === "id") {
      return BigInt(context.source);
    }
    return value;
  }
);
console.log(bigNum.id); // 9007199254740993n (BigInt)

These reviver techniques cover structured parsing, but invalid JSON input is just as common as tricky data types. JSON.parse() throws a SyntaxError on malformed input—trailing commas, single-quoted strings, and unquoted keys all fail. Knowing the exact error messages saves debug time when data comes from an external source.

Common Errors

// Trailing comma - throws SyntaxError
try {
  JSON.parse("[1, 2, 3,]");
} catch (e) {
  console.log(e.message);
  // "Unexpected token ] in JSON at position 11"
}

// Single quotes - throws SyntaxError
try {
  JSON.parse("{'foo': 1}");
} catch (e) {
  console.log(e.message);
  // "Unexpected token ' in JSON at position 1"
}

// Valid: use single quotes for JS string, double for JSON
console.log(JSON.parse('{"x": 1}')); // { x: 1 }

With error handling patterns established, it is worth examining how JSON.parse() integrates with common browser APIs. Reading stored data from localStorage is one of the most frequent use cases—values are always strings, so deserialization is mandatory before you can work with them.

Common Patterns

Reading from localStorage

const stored = localStorage.getItem("userPreferences");
if (stored) {
  const prefs = JSON.parse(stored);
  // Use prefs object
}

localStorage stores user preferences locally in the browser. For data that lives on a remote server, the fetch API pairs naturally with JSON.parse()—the response body arrives as a string and needs explicit deserialization before your code can work with the result.

Fetching and parsing API responses

async function fetchData(url) {
  const response = await fetch(url);
  const text = await response.text();
  return JSON.parse(text);
}

Direct fetch-and-parse works when you trust the API response format. Production code, however, often encounters malformed JSON, network truncations, or unexpected empty bodies. A wrapper that catches parse failures and returns a fallback value keeps the application running instead of crashing on bad input.

Safe parsing with default

function parseJSON(str, fallback = null) {
  try {
    return JSON.parse(str);
  } catch {
    return fallback;
  }
}

const data = parseJSON(invalidInput, { error: true });

Parsing with a default handles the failure case gracefully. The next step up is custom type reconstruction: if your application uses Map, Set, or Date objects, you can teach JSON.parse() to rebuild those types through the reviver. Embed a type marker during JSON.stringify() and decode it during JSON.parse() to preserve structure across serialization round-trips.

Preserving type information for round-trips

// Serialize Map with type marker
const map = new Map([["key", "value"]]);
const json = JSON.stringify(map, (k, v) => 
  v instanceof Map ? { __type: "Map", entries: [...v] } : v
);

// Revive Map
const restored = JSON.parse(json, (k, v) => 
  v?.__type === "Map" ? new Map(v.entries) : v
);
console.log(restored); // Map { "key" => "value" }

What counts as valid JSON

JSON is a strict subset of JavaScript. The following are valid JSON values: objects ({}), arrays ([]), strings (double-quoted only), numbers (no NaN, no Infinity, no leading zeros except 0 itself), true, false, and null. Invalid JSON that JSON.parse() will reject includes: single-quoted strings, trailing commas in objects or arrays, comments (// or /* */), undefined, NaN, Infinity, and bare identifiers. When parsing untrusted JSON (from external APIs or user input), always wrap JSON.parse() in a try/catch.

JSON.parse() and prototype pollution

Parsing untrusted JSON with a reviver function that blindly copies properties can expose the application to prototype pollution attacks, where an attacker crafts JSON like {"__proto__": {"isAdmin": true}}. The base JSON.parse() without a reviver is safe because it creates plain objects and does not traverse the prototype chain. If you use a reviver, be careful not to merge parsed properties onto existing objects without filtering keys like __proto__, constructor, and prototype.

Number precision in JSON

JSON numbers are parsed as JavaScript Number values (64-bit floats). Any integer larger than Number.MAX_SAFE_INTEGER (2^53 - 1) may lose precision silently during parsing — the JSON string is correct but the resulting JavaScript number is an approximation. The context.source argument in the reviver (available in modern engines) lets you read the original raw string for the value before it is converted, which is how you can safely parse large integers as BigInt.

See Also

  • JSON.stringify() — Convert JavaScript values to JSON strings
  • BigInt — Handle integers beyond Number.MAX_SAFE_INTEGER